﻿#region abl推流版本
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Web;
using Newtonsoft.Json;
using RestSharp;
using SIPSorcery.Net;
using SIPSorcery.SIP;
using Timer = System.Timers.Timer;

namespace GB28181Client
{
    class Program
    {
        private static RTPChannel? _rtpChannel;
        private static SIPTransport? _sipTransport;
        private static Process? _ffmpegProcess;
        private static Timer? _keepAliveTimer; // 添加定时器用于心跳
        private static int _keepAliveInterval = 60000; // 默认心跳间隔60秒（单位：毫秒）
        private static int _keepAliveSeq = 1; // 心跳序列号
        private static string ssrc = "";


        private static string _deviceId = "34020000001320000005"; // 设备 ID  摄像机唯一标识虚拟
        private static string _serverIp = "212.64.34.165"; // 服务端 IP
        private static int _serverPort = 15060; // 服务端端口
        private static string _serverId = "34020000002000000001"; // 服务器Id
        private static string _serverpassword = "12345678"; // 服务器密码

        private static string _localIp = "192.168.10.141"; // 设备本地 IP
        private static int _localRtpPort = 20002; // 本地 RTP 端口
        private static string _mp4FilePath = Path.Combine(AppContext.BaseDirectory, "Temp", "EasyDarwin.mp4"); // MP4 文件路径
        //private static string _mp4RecordFilePath = Path.Combine(AppContext.BaseDirectory, "Temp", "Record.mp4");
        private static string _mp4RecordFilePath = Path.Combine(AppContext.BaseDirectory, "Temp", "You.mp4");


        //private static string _deviceId = "34020000001320000002"; // 设备 ID
        //private static string _serverIp = "10.168.1.128"; // 服务端 IP
        //private static int _serverPort = 15060; // 服务端端口
        //private static string _serverId = "34020000002000000001"; // 服务器Id
        //private static string _serverpassword = "ldsw1234"; // 服务器密码

        //private static string _localIp = "10.168.1.30"; // 设备本地 IP
        //private static int _localRtpPort = 20002; // 本地 RTP 端口
        //private static string _mp4FilePath = Path.Combine(AppContext.BaseDirectory, "Temp", "EasyDarwin.mp4");
        //private static string _mp4RecordFilePath = Path.Combine(AppContext.BaseDirectory, "Temp", "Record.mp4");


        private static string _ablIp = "192.168.10.141";
        private static int ablPort = 7088;
        private static string _ablUrl = $"http://{_ablIp}:{ablPort}";

        static async Task Main(string[] args)
        {
            Console.WriteLine("Starting GB28181 Client...");

            // 注册编码支持（包括 GB2312）
            System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

            // 初始化 SIP 传输层
            _sipTransport = new SIPTransport();
            _sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.Any, 0))); // 随机本地端口
            _sipTransport.AddSIPChannel(new SIPTCPChannel(new IPEndPoint(IPAddress.Any, _localRtpPort))); // TCP通道，使用固定端口

            // 监听所有 SIP 请求和响应
            _sipTransport.SIPTransportRequestReceived += OnRequestReceived;
            _sipTransport.SIPTransportResponseReceived += OnResponseReceived;

            // 发送 REGISTER 请求
            await SendRegisterRequest(false); // 第一次不带认证头

            // 初始化并启动心跳保活
            StartKeepAlive(_keepAliveInterval);

            Console.WriteLine("Press any key to exit...");
            Console.ReadLine();

            // 清理资源
            StopKeepAlive();
            _sipTransport.Shutdown();
        }

        // 启动心跳保活
        private static void StartKeepAlive(int interval)
        {
            _keepAliveTimer = new Timer(interval);
            _keepAliveTimer.Elapsed += async (sender, e) => await SendKeepAlive();
            _keepAliveTimer.AutoReset = true;
            _keepAliveTimer.Start();
            Console.WriteLine($"KeepAlive started with interval: {interval}ms");
        }

        // 停止心跳保活
        private static void StopKeepAlive()
        {
            if (_keepAliveTimer != null)
            {
                _keepAliveTimer.Stop();
                _keepAliveTimer.Dispose();
                Console.WriteLine("KeepAlive stopped");
            }
        }

        // 发送心跳保活消息
        private static async Task SendKeepAlive()
        {
            var localEndPoint = _sipTransport.GetSIPChannels()[0].ListeningSIPEndPoint;
            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
            var keepAliveRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

            keepAliveRequest.Header = new SIPHeader();
            keepAliveRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
            keepAliveRequest.Header.To = new SIPToHeader(null, sipUri, null);
            keepAliveRequest.Header.CallId = CallProperties.CreateNewCallId();
            keepAliveRequest.Header.CSeq = _keepAliveSeq++;
            keepAliveRequest.Header.MaxForwards = 70;
            keepAliveRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
            keepAliveRequest.Header.ContentType = "Application/MANSCDP+xml";
            keepAliveRequest.Header.Vias.Via = new List<SIPViaHeader>
            {
                new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
            };

            string keepAliveXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
<Notify>
    <CmdType>Keepalive</CmdType>
    <SN>{_keepAliveSeq}</SN>
    <DeviceID>{_deviceId}</DeviceID>
    <Status>OK</Status>
</Notify>";

            keepAliveRequest.Body = keepAliveXml;
            keepAliveRequest.Header.ContentLength = Encoding.UTF8.GetBytes(keepAliveXml).Length;

            var remoteEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(IPAddress.Parse(_serverIp), _serverPort));
            Console.WriteLine($"Sending KeepAlive to {remoteEndPoint}");
            await _sipTransport.SendRequestAsync(remoteEndPoint, keepAliveRequest);
        }

        // 处理接收到的SIP请求
        private static async Task OnRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
        {
            Console.WriteLine($"Received request: {request.Method} from {remoteEndPoint}");

            if (request.Method == SIPMethodsEnum.MESSAGE)
            {
                // 检查是否为DeviceInfo查询
                string content = request.Body;
                if (content.Contains("<CmdType>DeviceInfo</CmdType>"))
                {
                    // 第一次响应：发送200 OK
                    await SendOkResponse(localSIPEndPoint, remoteEndPoint, request);
                    await SendDeviceInfoResponse(localSIPEndPoint, remoteEndPoint, request);
                }
                else if (content.Contains("<CmdType>DeviceStatus</CmdType>"))  // 新增 DeviceStatus 处理
                {
                    await SendOkResponse(localSIPEndPoint, remoteEndPoint, request);
                    await SendDeviceStatusResponse(localSIPEndPoint, remoteEndPoint, request);
                }
                else if (content.Contains("<CmdType>Catalog</CmdType>")) // 添加 Catalog 处理
                {
                    await SendOkResponse(localSIPEndPoint, remoteEndPoint, request);
                    await SendCatalogResponse(localSIPEndPoint, remoteEndPoint, request);
                }
                else if (content.Contains("<CmdType>RecordInfo</CmdType>"))
                {
                    await SendOkResponse(localSIPEndPoint, remoteEndPoint, request);
                    await SendRecordInfoResponse(localSIPEndPoint, remoteEndPoint, request);
                }
            }
            else if (request.Method == SIPMethodsEnum.SUBSCRIBE) // 新增 SUBSCRIBE 处理
            {
                string content = request.Body;
                if (content.Contains("<CmdType>Alarm</CmdType>"))
                {
                    await SendAlarmSubscriptionResponse(localSIPEndPoint, remoteEndPoint, request);
                }
                else if (content.Contains("<CmdType>MobilePosition</CmdType>")) // 新增移动位置订阅
                {
                    await SendMobilePositionSubscriptionResponse(localSIPEndPoint, remoteEndPoint, request);
                }
                else if (content.Contains("<CmdType>Catalog</CmdType>")) // 新增目录订阅
                {
                    await SendCatalogSubscriptionResponse(localSIPEndPoint, remoteEndPoint, request);
                }
            }
            else if (request.Method == SIPMethodsEnum.INVITE)
            {
                //判断是直播还是回放
                var sdp = SDP.ParseSDPDescription(request.Body);

                if (sdp.SessionName == "Playback")
                {
                    // 录像回放
                    await SendPlaybackInviteOkResponse(localSIPEndPoint, remoteEndPoint, request);
                    StartAblPushStream(request, _mp4RecordFilePath); // 使用 ABL 推流
                }
                else if (sdp.SessionName == "Play")
                {
                    // 1. 发送 100 Trying
                    await SendTryingResponse(localSIPEndPoint, remoteEndPoint, request);

                    // 2. 发送 200 OK 并提供 SDP
                    await SendInviteOkResponse(localSIPEndPoint, remoteEndPoint, request);

                    // 3. 启动 FFmpeg 推流
                    //StartFfmpegPushStream(request);

                    // 4.abl推流
                    StartAblPushStream(request, _mp4FilePath);
                }
                else if (sdp.SessionName == "Download")
                {
                    // 录像下载
                    await SendTryingResponse(localSIPEndPoint, remoteEndPoint, request);
                    await SendDownloadOkResponse(localSIPEndPoint, remoteEndPoint, request);
                    await StartDownloadPushStream(request, _mp4RecordFilePath); // 推送录像流
                }
            }
            else if (request.Method == SIPMethodsEnum.ACK)
            {
                Console.WriteLine("Received ACK, session established.");
            }
            else if (request.Method == SIPMethodsEnum.BYE)
            {
                await SendOkResponse(localSIPEndPoint, remoteEndPoint, request);
                if (_ffmpegProcess != null && !_ffmpegProcess.HasExited)
                {
                    _ffmpegProcess.Kill();
                    Console.WriteLine("FFmpeg process terminated due to BYE.");
                }
            }

            await Task.CompletedTask;
        }

        // 发送 200 OK响应方法
        private static async Task SendOkResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
        {
            SIPResponse okResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Ok, "OK");
            okResponse.Header.To.ToTag = CallProperties.CreateNewTag();
            okResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
            okResponse.Header.ContentLength = 0;

            Console.WriteLine($"Sending 200 OK response to {remoteEndPoint}");
            await _sipTransport.SendResponseAsync(okResponse);
        }

        // 发送 DeviceInfo 响应方法
        private static async Task SendDeviceInfoResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
        {
            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
            var deviceInfoRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

            deviceInfoRequest.Header = new SIPHeader();
            deviceInfoRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
            deviceInfoRequest.Header.To = new SIPToHeader(null, sipUri, null);
            deviceInfoRequest.Header.CallId = CallProperties.CreateNewCallId();
            deviceInfoRequest.Header.CSeq = 1;
            deviceInfoRequest.Header.MaxForwards = 70;
            deviceInfoRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
            deviceInfoRequest.Header.ContentType = "Application/MANSCDP+xml";
            deviceInfoRequest.Header.Vias.Via = new List<SIPViaHeader>
            {
                new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
            };

            string sn = GetSNFromRequest(request.Body);
            string deviceInfoXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
<Response>
    <CmdType>DeviceInfo</CmdType>
    <SN>{sn}</SN>
    <DeviceID>{_deviceId}</DeviceID>
    <DeviceName>EasyGBD-069600</DeviceName>
    <Result>OK</Result>
    <Manufacturer>hhh</Manufacturer>
    <Model>EasyGBD</Model>
    <Firmware>V2.0</Firmware>
    <Channel>1</Channel>
</Response>";

            deviceInfoRequest.Body = deviceInfoXml;
            deviceInfoRequest.Header.ContentLength = Encoding.UTF8.GetBytes(deviceInfoXml).Length;

            Console.WriteLine($"Sending DeviceInfo response to {remoteEndPoint}");
            await _sipTransport.SendRequestAsync(remoteEndPoint, deviceInfoRequest);
        }

        //发送 DeviceStatus 响应方法
        private static async Task SendDeviceStatusResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
        {
            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
            var deviceStatusRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

            deviceStatusRequest.Header = new SIPHeader();
            deviceStatusRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
            deviceStatusRequest.Header.To = new SIPToHeader(null, sipUri, null);
            deviceStatusRequest.Header.CallId = CallProperties.CreateNewCallId();
            deviceStatusRequest.Header.CSeq = 1;
            deviceStatusRequest.Header.MaxForwards = 70;
            deviceStatusRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
            deviceStatusRequest.Header.ContentType = "Application/MANSCDP+xml";
            deviceStatusRequest.Header.Vias.Via = new List<SIPViaHeader>
    {
        new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
    };

            string sn = GetSNFromRequest(request.Body);
            string currentTime = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss");
            string deviceStatusXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
<Response>
    <CmdType>DeviceStatus</CmdType>
    <SN>{sn}</SN>
    <DeviceID>{_deviceId}</DeviceID>
    <Result>OK</Result>
    <Online>ONLINE</Online>
    <Status>OK</Status>
    <Encode>ON</Encode>
    <Record>ON</Record>
    <DeviceTime>{currentTime}</DeviceTime>
    <Alarmstatus Num=""0"">
    </Alarmstatus>
</Response>";

            deviceStatusRequest.Body = deviceStatusXml;
            deviceStatusRequest.Header.ContentLength = Encoding.UTF8.GetBytes(deviceStatusXml).Length;

            Console.WriteLine($"Sending DeviceStatus response to {remoteEndPoint}");
            await _sipTransport.SendRequestAsync(remoteEndPoint, deviceStatusRequest);
        }

        // 新增 Catalog 响应方法
        private static async Task SendCatalogResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
        {
            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
            var catalogRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

            catalogRequest.Header = new SIPHeader();
            catalogRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
            catalogRequest.Header.To = new SIPToHeader(null, sipUri, null);
            catalogRequest.Header.CallId = CallProperties.CreateNewCallId();
            catalogRequest.Header.CSeq = 1;
            catalogRequest.Header.MaxForwards = 70;
            catalogRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
            catalogRequest.Header.ContentType = "Application/MANSCDP+xml";
            catalogRequest.Header.Vias.Via = new List<SIPViaHeader>
            {
                new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
            };

            string sn = GetSNFromRequest(request.Body);
            string catalogXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
<Response>
    <CmdType>Catalog</CmdType>
    <SN>{sn}</SN>
    <DeviceID>{_deviceId}</DeviceID>
    <SumNum>1</SumNum>
    <DeviceList Num=""1"">
        <Item>
            <DeviceID>34020000001310000001</DeviceID>
            <Name>EasyIPC</Name>
            <Manufacturer>TSINGSEE</Manufacturer>
            <Model>EasyGBD</Model>
            <Owner>Owner</Owner>
            <CivilCode>3402000000</CivilCode>
            <Address>Address</Address>
            <Parental>0</Parental>
            <ParentID>{_deviceId}</ParentID>
            <RegisterWay>1</RegisterWay>
            <Secrecy>0</Secrecy>
            <Status>ON</Status>
        </Item>
    </DeviceList>
</Response>";

            catalogRequest.Body = catalogXml;
            catalogRequest.Header.ContentLength = Encoding.UTF8.GetBytes(catalogXml).Length;

            Console.WriteLine($"Sending Catalog response to {remoteEndPoint}");
            await _sipTransport.SendRequestAsync(remoteEndPoint, catalogRequest);
        }

        //发送 AlarmSubscription 响应方法
        private static async Task SendAlarmSubscriptionResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
        {
            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
            var alarmRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

            alarmRequest.Header = new SIPHeader();
            alarmRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
            alarmRequest.Header.To = new SIPToHeader(null, sipUri, null);
            alarmRequest.Header.CallId = CallProperties.CreateNewCallId(); // 新生成 Call-ID
            alarmRequest.Header.CSeq = 1; // CSeq 从 1 开始
            alarmRequest.Header.MaxForwards = 70;
            alarmRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
            alarmRequest.Header.ContentType = "Application/MANSCDP+xml";
            alarmRequest.Header.Vias.Via = new List<SIPViaHeader>
    {
        new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
    };
            alarmRequest.Header.Event = "presence"; // 与请求中的 Event 一致
            alarmRequest.Header.Expires = 0; // 与请求中的 Expires 一致

            // 提取请求中的 SN 并构造告警订阅的 XML
            string sn = GetSNFromRequest(request.Body);
            string alarmXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
<Response>
    <CmdType>Alarm</CmdType>
    <SN>{sn}</SN>
    <DeviceID>{_deviceId}</DeviceID>
    <Result>OK</Result>
</Response>";

            // 设置正文和长度，使用 GB2312 编码（需确保已注册）
            alarmRequest.Body = alarmXml;
            alarmRequest.Header.ContentLength = Encoding.GetEncoding("GB2312").GetBytes(alarmXml).Length;

            // 发送告警订阅的 MESSAGE 请求
            Console.WriteLine($"Sending Alarm subscription MESSAGE to {remoteEndPoint}");
            await _sipTransport.SendRequestAsync(remoteEndPoint, alarmRequest);
        }

        //发送 DeviceControl 响应方法
        private static async Task SendDeviceControlResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
        {
            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
            var controlResponse = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

            controlResponse.Header = new SIPHeader();
            controlResponse.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
            controlResponse.Header.To = new SIPToHeader(null, sipUri, null);
            controlResponse.Header.CallId = CallProperties.CreateNewCallId();
            controlResponse.Header.CSeq = 1;
            controlResponse.Header.MaxForwards = 70;
            controlResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
            controlResponse.Header.ContentType = "Application/MANSCDP+xml";
            controlResponse.Header.Vias.Via = new List<SIPViaHeader>
    {
        new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
    };

            string sn = GetSNFromRequest(request.Body);
            string controlXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
<Response>
    <CmdType>DeviceControl</CmdType>
    <SN>{sn}</SN>
    <DeviceID>{_deviceId}</DeviceID>
    <Result>OK</Result>
</Response>";

            controlResponse.Body = controlXml;
            controlResponse.Header.ContentLength = Encoding.UTF8.GetBytes(controlXml).Length;

            Console.WriteLine($"Sending DeviceControl response to {remoteEndPoint}");
            await _sipTransport.SendRequestAsync(remoteEndPoint, controlResponse);
        }

        //发送 MobilePositionSubscription 响应方法
        private static async Task SendMobilePositionSubscriptionResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
        {
            // 2. 构造并发送独立的 MESSAGE 请求推送移动位置订阅响应
            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
            var mobileRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

            // 设置 SIP 头
            mobileRequest.Header = new SIPHeader();
            mobileRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
            mobileRequest.Header.To = new SIPToHeader(null, sipUri, null);
            mobileRequest.Header.CallId = CallProperties.CreateNewCallId(); // 新生成 Call-ID
            mobileRequest.Header.CSeq = 1; // CSeq 从 1 开始
            mobileRequest.Header.MaxForwards = 70;
            mobileRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
            mobileRequest.Header.ContentType = "Application/MANSCDP+xml";
            mobileRequest.Header.Vias.Via = new List<SIPViaHeader>
            {
                new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
            };
            mobileRequest.Header.Event = "presence"; // 与请求中的 Event 一致
            mobileRequest.Header.Expires = request.Header.Expires; // 与请求中的 Expires 一致

            // 提取请求中的 SN 并构造移动位置订阅的 XML
            string sn = GetSNFromRequest(request.Body);
            string mobileXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
<Response>
    <CmdType>MobilePosition</CmdType>
    <SN>{sn}</SN>
    <DeviceID>{_deviceId}</DeviceID>
    <Result>OK</Result>
</Response>";

            // 设置正文和长度，使用 GB2312 编码（需确保已注册）
            mobileRequest.Body = mobileXml;
            mobileRequest.Header.ContentLength = Encoding.GetEncoding("GB2312").GetBytes(mobileXml).Length;

            // 发送移动位置订阅的 MESSAGE 请求
            Console.WriteLine($"Sending MobilePosition subscription MESSAGE to {remoteEndPoint}");
            await _sipTransport.SendRequestAsync(remoteEndPoint, mobileRequest);
        }

        //发送 CatalogSubscription 响应方法
        private static async Task SendCatalogSubscriptionResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
        {
            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
            var catalogRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

            // 设置 SIP 头
            catalogRequest.Header = new SIPHeader();
            catalogRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
            catalogRequest.Header.To = new SIPToHeader(null, sipUri, null);
            catalogRequest.Header.CallId = CallProperties.CreateNewCallId(); // 新生成 Call-ID
            catalogRequest.Header.CSeq = 1; // CSeq 从 1 开始
            catalogRequest.Header.MaxForwards = 70;
            catalogRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
            catalogRequest.Header.ContentType = "Application/MANSCDP+xml";
            catalogRequest.Header.Vias.Via = new List<SIPViaHeader>
    {
        new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
    };
            catalogRequest.Header.Event = request.Header.Event; // 保持与请求一致，如 "Catalog;id=41831755"
            catalogRequest.Header.Expires = request.Header.Expires; // 与请求中的 Expires 一致
            catalogRequest.Header.Contact = new List<SIPContactHeader>
    {
        new SIPContactHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{localEndPoint.Address}:{localEndPoint.Port}"))
    };

            // 提取请求中的 SN 并构造目录订阅的 XML
            string sn = GetSNFromRequest(request.Body);
            string catalogXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
<Response>
    <CmdType>Catalog</CmdType>
    <SN>{sn}</SN>
    <DeviceID>{_deviceId}</DeviceID>
    <Result>OK</Result>
</Response>";

            // 设置正文和长度，使用 GB2312 编码（需确保已注册）
            catalogRequest.Body = catalogXml;
            catalogRequest.Header.ContentLength = Encoding.GetEncoding("GB2312").GetBytes(catalogXml).Length;

            // 发送目录订阅的 MESSAGE 请求
            Console.WriteLine($"Sending Catalog subscription MESSAGE to {remoteEndPoint}");
            await _sipTransport.SendRequestAsync(remoteEndPoint, catalogRequest);
        }

        //发送 RecordInfo 响应方法
        private static async Task SendRecordInfoResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
        {
            // 构造目标 SIP URI
            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
            var recordInfoRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

            // 设置 SIP 头
            recordInfoRequest.Header = new SIPHeader();
            recordInfoRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
            recordInfoRequest.Header.To = new SIPToHeader(null, sipUri, null);
            recordInfoRequest.Header.CallId = CallProperties.CreateNewCallId(); // 新生成 Call-ID
            recordInfoRequest.Header.CSeq = 1; // CSeq 从 1 开始
            recordInfoRequest.Header.MaxForwards = 70;
            recordInfoRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
            recordInfoRequest.Header.ContentType = "Application/MANSCDP+xml";
            recordInfoRequest.Header.Vias.Via = new List<SIPViaHeader>
    {
        new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
    };

            // 提取 SN 并构造录像列表 XML
            string sn = GetSNFromRequest(request.Body);
            string recordInfoXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
<Response>
    <CmdType>RecordInfo</CmdType>
    <SN>{sn}</SN>
    <DeviceID>34020000001310000001</DeviceID>
    <Name>EasyGBD-069600</Name>
    <SumNum>2</SumNum>
    <RecordList Num=""2"">
    <Item>
    <DeviceID>34020000001310000001</DeviceID>
    <Name>EasyGBD-069600</Name>
    <Address></Address>
    <StartTime>2025-03-30T00:00:00</StartTime>
    <EndTime>2025-03-30T00:00:03</EndTime>
    <Secrecy>0</Secrecy>
    <Type>all</Type>
    <FileSize>630528</FileSize>
    </Item>
    <Item>
    <DeviceID>34020000001310000001</DeviceID>
    <Name>EasyGBD-069600</Name>
    <Address></Address>
    <StartTime>2025-03-30T00:10:00</StartTime>
    <EndTime>2025-03-30T00:10:03</EndTime>
    <Secrecy>0</Secrecy>
    <Type>all</Type>
    <FileSize>630528</FileSize>
    </Item>
    </RecordList>
</Response>";

            // 设置正文和长度
            recordInfoRequest.Body = recordInfoXml;
            recordInfoRequest.Header.ContentLength = Encoding.GetEncoding("GB2312").GetBytes(recordInfoXml).Length;

            // 发送录像列表请求
            Console.WriteLine($"Sending RecordInfo MESSAGE to {remoteEndPoint}");
            await _sipTransport.SendRequestAsync(remoteEndPoint, recordInfoRequest);
        }

        //发送 100 try 响应方法
        private static async Task SendTryingResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
        {
            SIPResponse tryingResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Trying, "Trying");
            tryingResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
            tryingResponse.Header.ContentLength = 0;

            Console.WriteLine($"Sending 100 Trying to {remoteEndPoint}");
            await _sipTransport.SendResponseAsync(tryingResponse);
        }

        //发送 Invite 响应方法
        private static async Task SendInviteOkResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
        {
            SIPResponse okResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Ok, "OK");
            okResponse.Header.To.ToTag = CallProperties.CreateNewTag();
            okResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
            okResponse.Header.Contact = new List<SIPContactHeader>
            {
                new SIPContactHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_localIp}"))
            };
            okResponse.Header.ContentType = "application/sdp";

            // 解析服务器 SDP
            string[] sdpLines = request.Body.Split('\n');
            string serverIp = sdpLines.FirstOrDefault(l => l.StartsWith("c="))?.Split(' ').LastOrDefault()?.Trim() ?? _serverIp;
            string mediaLine = sdpLines.FirstOrDefault(l => l.StartsWith("m=video"));
            int serverRtpPort = int.Parse(mediaLine?.Split(' ')[1] ?? "30264");
            ssrc = sdpLines.FirstOrDefault(l => l.StartsWith("y="))?.Substring(2).Replace("\r", ""); // 获取 INVITE 的 SSRC

            var time = (request.Body.Split('\n').FirstOrDefault(l => l.StartsWith("t="))?.Trim() ?? "0 0").Replace("t=", "");

            Console.WriteLine($"Received INVITE SDP:\n{request.Body}");

            //m=video {_localRtpPort} TCP/RTP/AVP 96

            // 生成设备的 SDP，使用 RTP/AVP
            string sdp = $@"v=0
o={_deviceId} 0 0 IN IP4 {_localIp}
s=Play
c=IN IP4 {_localIp}
t={time}
m=video {_localRtpPort} RTP/AVP 98
a=rtpmap:98 H264/90000
a=sendonly
y={ssrc}
f=v/2////a///";

            okResponse.Body = sdp;
            okResponse.Header.ContentLength = Encoding.UTF8.GetBytes(sdp).Length;

            Console.WriteLine($"Sending 200 OK with SDP to {remoteEndPoint}");
            Console.WriteLine($"SDP Content:\n{sdp}");
            await _sipTransport.SendResponseAsync(okResponse);
        }

        private static async Task SendPlaybackInviteOkResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
        {
            SIPResponse okResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Ok, "OK");
            okResponse.Header.To.ToTag = CallProperties.CreateNewTag();
            okResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
            okResponse.Header.Contact = new List<SIPContactHeader>
            {
                new SIPContactHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_localIp}"))
            };
            okResponse.Header.ContentType = "application/sdp";

            // 解析服务器 SDP
            string[] sdpLines = request.Body.Split('\n');
            string serverIp = sdpLines.FirstOrDefault(l => l.StartsWith("c="))?.Split(' ').LastOrDefault()?.Trim() ?? _serverIp;
            string mediaLine = sdpLines.FirstOrDefault(l => l.StartsWith("m=video"));
            int serverRtpPort = int.Parse(mediaLine?.Split(' ')[1] ?? "30264");
            ssrc = sdpLines.FirstOrDefault(l => l.StartsWith("y="))?.Substring(2).Replace("\r", ""); // 获取 SSRC
            string timeLine = (sdpLines.FirstOrDefault(l => l.StartsWith("t="))?.Trim() ?? "0 0").Replace("t=", "");

            Console.WriteLine($"Received Playback INVITE SDP:\n{request.Body}");

            // 生成设备的 SDP
            string sdp = $@"v=0
o={_deviceId} 0 0 IN IP4 {_localIp}
s=Playback
c=IN IP4 {_localIp}
t={timeLine}
m=video {_localRtpPort} RTP/AVP 96
a=rtpmap:96 PS/90000
a=sendonly
y={ssrc}
f=v/2////a///";

            okResponse.Body = sdp;
            okResponse.Header.ContentLength = Encoding.UTF8.GetBytes(sdp).Length;

            Console.WriteLine($"Sending 200 OK with Playback SDP to {remoteEndPoint}");
            Console.WriteLine($"SDP Content:\n{sdp}");
            await _sipTransport.SendResponseAsync(okResponse);
        }

        private static async Task SendDownloadOkResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
        {
            SIPResponse okResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Ok, "OK");
            okResponse.Header.To.ToTag = CallProperties.CreateNewTag();
            okResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
            okResponse.Header.Contact = new List<SIPContactHeader>
            {
                new SIPContactHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_localIp}"))
            };
            okResponse.Header.ContentType = "application/sdp";

            // 解析服务器SDP
            string[] sdpLines = request.Body.Split('\n');
            string serverIp = sdpLines.FirstOrDefault(l => l.StartsWith("c="))?.Split(' ').LastOrDefault()?.Trim() ?? _serverIp;
            string mediaLine = sdpLines.FirstOrDefault(l => l.StartsWith("m=video"));
            int serverRtpPort = int.Parse(mediaLine?.Split(' ')[1] ?? "30184");
            ssrc = sdpLines.FirstOrDefault(l => l.StartsWith("y="))?.Substring(2).Replace("\r", "");
            string timeLine = (sdpLines.FirstOrDefault(l => l.StartsWith("t="))?.Trim() ?? "0 0").Replace("t=", "");
            string downloadSpeed = (sdpLines.FirstOrDefault(l => l.StartsWith("a=downloadspeed"))?.Split(':').LastOrDefault() ?? "4").Replace("\r", "");

            Console.WriteLine($"Received Download INVITE SDP:\n{request.Body}");

            // 构造本地SDP，使用TCP/RTP/AVP
            string sdp = $@"v=0
o={_deviceId} 0 0 IN IP4 {_localIp}
s=Download
c=IN IP4 {_localIp}
t={timeLine}
m=video {_localRtpPort} TCP/RTP/AVP 96
a=rtpmap:96 PS/90000
a=sendonly
a=setup:active
a=connection:new
a=downloadspeed:{downloadSpeed}
a=filesize:{new FileInfo(_mp4RecordFilePath).Length}
y={ssrc}";

            okResponse.Body = sdp;
            okResponse.Header.ContentLength = Encoding.UTF8.GetBytes(sdp).Length;

            Console.WriteLine($"Sending 200 OK with Download SDP to {remoteEndPoint}");
            Console.WriteLine($"SDP Content:\n{sdp}");
            await _sipTransport.SendResponseAsync(okResponse);
        }

        private static async Task StartDownloadPushStream(SIPRequest request, string filePath)
        {
            string[] sdpLines = request.Body.Split('\n');
            string serverIp = sdpLines.FirstOrDefault(l => l.StartsWith("c="))?.Split(' ').LastOrDefault()?.Trim() ?? _serverIp;
            string mediaLine = sdpLines.FirstOrDefault(l => l.StartsWith("m=video"));
            int serverRtpPort = int.Parse(mediaLine?.Split(' ')[1] ?? "30184");
            ssrc = sdpLines.FirstOrDefault(l => l.StartsWith("y="))?.Substring(2).Replace("\r", "");
            string downloadSpeed = (sdpLines.FirstOrDefault(l => l.StartsWith("a=downloadspeed"))?.Split(':').LastOrDefault() ?? "4").Replace("\r", "");

            // 根据 SDP 时间范围裁剪（假设 t= 的单位为秒）
            string[] timeRange = sdpLines.FirstOrDefault(l => l.StartsWith("t="))?.Split(' ') ?? new[] { "0", "0" };
            double startTime = double.Parse(timeRange[0].Replace("t=", "")); // 开始时间戳
            double endTime = double.Parse(timeRange[1].Replace("\r", ""));   // 结束时间戳
            double duration = (endTime - startTime) / int.Parse(downloadSpeed); // 调整为倍速后的时长

            string ffmpegArgs = $"-i \"{filePath}\" -vf \"setpts=PTS/{downloadSpeed}\" -c:v libx264 -b:v 2000k -preset ultrafast -an -t {duration} -f mpegts -loglevel verbose pipe:";

            ProcessStartInfo psi = new ProcessStartInfo
            {
                FileName = "ffmpeg",
                Arguments = ffmpegArgs,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };

            using (var tcpClient = new System.Net.Sockets.TcpClient())
            {
                try
                {
                    Console.WriteLine($"Connecting to {serverIp}:{serverRtpPort}...");
                    await tcpClient.ConnectAsync(serverIp, serverRtpPort);
                    using (var stream = tcpClient.GetStream())
                    {
                        _ffmpegProcess = new Process { StartInfo = psi };
                        _ffmpegProcess.EnableRaisingEvents = true;

                        _ffmpegProcess.ErrorDataReceived += (sender, e) =>
                        {
                            if (e.Data != null)
                            {
                                if (e.Data.Contains("Error") || e.Data.Contains("failed") || e.Data.Contains("Invalid"))
                                    Console.WriteLine($"FFmpeg Error: {e.Data}");
                                else
                                    Console.WriteLine($"FFmpeg Info: {e.Data}");
                            }
                        };
                        _ffmpegProcess.Exited += async (sender, e) =>
                        {
                            Console.WriteLine($"FFmpeg download process exited with code: {_ffmpegProcess.ExitCode}");
                            await SendMediaStatusNotification(request, "121");
                        };

                        Console.WriteLine("Starting FFmpeg process...");
                        _ffmpegProcess.Start();
                        _ffmpegProcess.BeginErrorReadLine();

                        byte[] tsBuffer = new byte[188 * 7]; // 1316字节
                        int bytesRead;
                        uint sequenceNumber = 0;
                        uint timestamp = 0;
                        long totalBytesSent = 0;

                        Console.WriteLine("Starting to read FFmpeg output...");
                        while ((bytesRead = await _ffmpegProcess.StandardOutput.BaseStream.ReadAsync(tsBuffer, 0, tsBuffer.Length)) > 0)
                        {
                            // 构造RTP头部
                            byte[] rtpHeader = new byte[12];
                            rtpHeader[0] = 0x80; // Version 2
                            rtpHeader[1] = 96;   // Payload type 96 (PS)
                            BitConverter.GetBytes(sequenceNumber++).CopyTo(rtpHeader, 2);
                            BitConverter.GetBytes(timestamp).CopyTo(rtpHeader, 4);
                            BitConverter.GetBytes(uint.Parse(ssrc)).CopyTo(rtpHeader, 8);

                            timestamp += (uint)(90000 / 25 / int.Parse(downloadSpeed) * 7);

                            // 组合RTP包
                            byte[] rtpPacket = new byte[12 + bytesRead];
                            Array.Copy(rtpHeader, 0, rtpPacket, 0, 12);
                            Array.Copy(tsBuffer, 0, rtpPacket, 12, bytesRead);

                            await stream.WriteAsync(rtpPacket, 0, rtpPacket.Length);
                            await stream.FlushAsync();
                            totalBytesSent += rtpPacket.Length;
                            Console.WriteLine($"Sent RTP packet: {rtpPacket.Length} bytes, Total: {totalBytesSent / 1024} KB");
                        }
                        Console.WriteLine("FFmpeg output ended.");

                        // 发送 RTCP BYE 包
                        byte[] rtcpBye = new byte[] { 0x81, 0xCB, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 };
                        BitConverter.GetBytes(uint.Parse(ssrc)).CopyTo(rtcpBye, 4);
                        await stream.WriteAsync(rtcpBye, 0, rtcpBye.Length);
                        await stream.FlushAsync();
                        Console.WriteLine("Sent RTCP BYE packet.");

                        Console.WriteLine($"Completed TCP RTP stream to {serverIp}:{serverRtpPort}, Total sent: {totalBytesSent / 1024} KB");
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Error starting TCP push: {ex.Message}");
                }
            }
        }

        private static async Task SendMediaStatusNotification(SIPRequest request, string notifyType)
        {
            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
            var notifyRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

            notifyRequest.Header = new SIPHeader();
            notifyRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
            notifyRequest.Header.To = new SIPToHeader(null, sipUri, null);
            notifyRequest.Header.CallId = CallProperties.CreateNewCallId();
            notifyRequest.Header.CSeq = 1;
            notifyRequest.Header.MaxForwards = 70;
            notifyRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
            notifyRequest.Header.ContentType = "Application/MANSCDP+xml";
            notifyRequest.Header.Vias.Via = new List<SIPViaHeader>
    {
        new SIPViaHeader(_sipTransport.GetSIPChannels()[0].ListeningSIPEndPoint, CallProperties.CreateBranchId())
    };

            string notifyXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
<Notify>
    <CmdType>MediaStatus</CmdType>
    <SN>151</SN>
    <DeviceID>{_deviceId}</DeviceID>
    <NotifyType>{notifyType}</NotifyType>
</Notify>";

            notifyRequest.Body = notifyXml;
            notifyRequest.Header.ContentLength = Encoding.GetEncoding("GB2312").GetBytes(notifyXml).Length;

            var remoteEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(IPAddress.Parse(_serverIp), _serverPort));
            Console.WriteLine($"Sending MediaStatus ({notifyType}) to {remoteEndPoint}");
            await _sipTransport.SendRequestAsync(remoteEndPoint, notifyRequest);
        }

        //ffmpeg推流
        private static void StartFfmpegPushStream(SIPRequest request)
        {
            string[] sdpLines = request.Body.Split('\n');
            string serverIp = sdpLines.FirstOrDefault(l => l.StartsWith("c="))?.Split(' ').LastOrDefault()?.Trim() ?? _serverIp;
            string mediaLine = sdpLines.FirstOrDefault(l => l.StartsWith("m=video"));
            int serverRtpPort = int.Parse(mediaLine?.Split(' ')[1] ?? "30264"); // 从 INVITE SDP 获取服务器端口

            Console.WriteLine($"Parsed SDP: Server IP = {serverIp}, Port = {serverRtpPort}");

            // 使用 _localRtpPort 作为源端口，推送至服务器端口
            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -f mpegts -local_rtpport {_localRtpPort} \"udp://{serverIp}:{serverRtpPort}\"";
            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -f mpegts -local_rtpport {_localRtpPort} \"udp://{serverIp}:{serverRtpPort}\"";
            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -f rtsp -an \"rtsp://{serverIp}:{serverRtpPort}/stream\"";
            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v libx264 -b:v 2000k -r 25 -f mpegts -an -loglevel verbose \"udp://{serverIp}:{serverRtpPort}\"";
            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v libx264 -b:v 2000k -r 25 -c:a aac -b:a 128k -f rtp -payload_type 96 -ssrc {ssrc} \"rtp://{serverIp}:{serverRtpPort}\"";
            string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v libx264 -b:v 2000k -r 25 -profile:v baseline -level 3.0 -an -f rtp -payload_type 98 -ssrc {ssrc} \"rtp://{serverIp}:{serverRtpPort}\"";


            Console.WriteLine($"Starting FFmpeg push stream: ffmpeg {ffmpegArgs}");

            ProcessStartInfo psi = new ProcessStartInfo
            {
                FileName = "ffmpeg",
                Arguments = ffmpegArgs,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };

            try
            {
                _ffmpegProcess = new Process { StartInfo = psi };
                _ffmpegProcess.EnableRaisingEvents = true;

                _ffmpegProcess.OutputDataReceived += (sender, e) =>
                {
                    if (e.Data != null)
                        Console.WriteLine($"FFmpeg Output: {e.Data}");
                };
                _ffmpegProcess.ErrorDataReceived += (sender, e) =>
                {
                    if (e.Data != null)
                    {
                        // 检查是否包含错误关键词
                        if (e.Data.Contains("Error") || e.Data.Contains("failed") || e.Data.Contains("Invalid"))
                        {
                            Console.WriteLine($"FFmpeg Error: {e.Data}");
                        }
                        else
                        {
                            Console.WriteLine($"FFmpeg Info: {e.Data}");
                        }
                    }
                };
                _ffmpegProcess.Exited += (sender, e) =>
                {
                    Console.WriteLine($"FFmpeg process exited with code: {_ffmpegProcess.ExitCode}");
                };

                bool started = _ffmpegProcess.Start();
                if (started)
                {
                    Console.WriteLine("FFmpeg process started successfully.");
                    _ffmpegProcess.BeginOutputReadLine();
                    _ffmpegProcess.BeginErrorReadLine();
                }
                else
                {
                    Console.WriteLine("Failed to start FFmpeg process.");
                }

                Task.Run(async () =>
                {
                    await Task.Run(() => _ffmpegProcess.WaitForExit());
                    Console.WriteLine($"FFmpeg process completed with exit code: {_ffmpegProcess.ExitCode}");
                });
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error starting FFmpeg: {ex.Message}");
            }
        }

        //abl推流
        private static void StartAblPushStream(SIPRequest request, string filePath)
        {
            var client = new RestClient(_ablUrl);

            var request2 = new RestRequest("/index/api/addStreamProxy", Method.Post);

            var streamFlag = "";
            var guid = Guid.NewGuid().ToString(); //先不获取流列表 代理拉流
            if (filePath.Contains("Record"))
            {
                streamFlag = "Playback";
            }
            else
            {
                streamFlag = "Play";
            }
            var requestData = new
            {
                secret = "035c73f7-bb6b-4889-a715-d9eb2d1925cc",
                vhost = "_defaultVhost_",
                app = "gb28181",
                stream = $"Camera_00001_{streamFlag}_{guid}",
                url = filePath
            };

            var jsonBody = JsonConvert.SerializeObject(requestData);

            request2.AddHeader("Content-Type", "application/json");

            request2.AddParameter("application/json", jsonBody, ParameterType.RequestBody);

            var response2 = client.Execute(request2);

            if (response2.IsSuccessful)
            {
                Console.WriteLine($"加入abl代理拉流成功：{response2.StatusCode}");
            }
            else
            {
                Console.WriteLine($"加入abl代理拉流失败：{response2.ErrorMessage}");
            }

            //实时tcp主动推流

            // 解析服务器 SDP
            string[] sdpLines = request.Body.Split('\n');
            string serverIp = sdpLines.FirstOrDefault(l => l.StartsWith("c="))?.Split(' ').LastOrDefault()?.Trim() ?? _serverIp;
            string mediaLine = sdpLines.FirstOrDefault(l => l.StartsWith("m=video"));
            int serverRtpPort = int.Parse(mediaLine?.Split(' ')[1] ?? "30264");
            ssrc = sdpLines.FirstOrDefault(l => l.StartsWith("y="))?.Substring(2).Replace("\r", ""); // 获取 INVITE 的 SSRC

            var request3 = new RestRequest($"/index/api/startSendRtp?secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc&vhost=_defaultVhost_&app=gb28181&stream={requestData.stream}&payload=96&ssrc={ssrc}&src_port={_localIp}&dst_url={serverIp}&dst_port={serverRtpPort}&is_udp%20=0", Method.Get);
            var response3 = client.Execute(request3);
            Console.WriteLine($"abl-rtp拉流：{response3.StatusCode}");



        }

        // 从请求中提取SN
        private static string GetSNFromRequest(string requestBody)
        {
            try
            {
                int snStart = requestBody.IndexOf("<SN>") + 4;
                int snEnd = requestBody.IndexOf("</SN>");
                return requestBody.Substring(snStart, snEnd - snStart);
            }
            catch
            {
                return "0";
            }
        }

        // 发送 REGISTER 请求
        private static async Task SendRegisterRequest(bool withAuth, string authHeader = null)
        {
            var sipUri = SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}");
            var regRequest = new SIPRequest(SIPMethodsEnum.REGISTER, sipUri);

            regRequest.Header = new SIPHeader();

            // 获取本地端点
            var localEndPoint = _sipTransport.GetSIPChannels()[0].ListeningSIPEndPoint;
            if (localEndPoint == null)
            {
                Console.WriteLine("Local SIP endpoint is null!");
                return;
            }

            // 设置 From 和 To 头
            regRequest.Header.From = new SIPFromHeader(null, sipUri, null);
            regRequest.Header.To = new SIPToHeader(null, sipUri, null);
            regRequest.Header.Contact = new List<SIPContactHeader>
            {
                new SIPContactHeader(null, new SIPURI(SIPSchemesEnum.sip, localEndPoint.Address, localEndPoint.Port) { User = _deviceId })
            };
            regRequest.Header.Vias.Via = new List<SIPViaHeader>
            {
                new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
            };
            regRequest.Header.CSeq = withAuth ? 2 : 1;
            regRequest.Header.CallId = Guid.NewGuid().ToString();
            regRequest.Header.Expires = 3600;

            // 如果需要认证，设置 Authorization 头
            if (withAuth && authHeader != null)
            {
                regRequest.Header.AuthenticationHeaders = new List<SIPAuthenticationHeader>
                {
                    SIPAuthenticationHeader.ParseSIPAuthenticationHeader(SIPAuthorisationHeadersEnum.Authorize, authHeader)
                };
                Console.WriteLine($"Added Authorization header: {authHeader}");
            }

            // 指定目标端点并发送请求
            var dstEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(IPAddress.Parse(_serverIp), _serverPort));
            Console.WriteLine("Sending REGISTER request to: " + dstEndPoint);
            await _sipTransport.SendRequestAsync(dstEndPoint, regRequest);
        }

        // 处理服务端返回的响应
        private static Task OnResponseReceived(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPResponse response)
        {
            Console.WriteLine($"Received response: {response.StatusCode} {response.ReasonPhrase}");

            if (response.Status == SIPResponseStatusCodesEnum.Unauthorised)
            {
                if (response.Header.AuthenticationHeaders != null && response.Header.AuthenticationHeaders.Count > 0)
                {
                    var wwwAuthHeader = response.Header.AuthenticationHeaders[0];
                    if (wwwAuthHeader != null)
                    {
                        string authHeader = ComputeDigestAuth(wwwAuthHeader, _deviceId, _serverpassword, "REGISTER", $"sip:{_serverId}@{wwwAuthHeader.SIPDigest.Realm}");
                        Console.WriteLine($"Generated Authorization: {authHeader}");
                        Task.Run(() => SendRegisterRequest(true, authHeader));
                    }
                }
                else
                {
                    Console.WriteLine("No WWW-Authenticate header found in 401 response.");
                }
            }
            else if (response.Status == SIPResponseStatusCodesEnum.Ok)
            {
                Console.WriteLine("服务器响应ok");
            }
            else
            {
                Console.WriteLine($"Unexpected response: {response.Status}");
            }

            return Task.CompletedTask;
        }

        //md5加密
        private static string ComputeDigestAuth(SIPAuthenticationHeader authHeader, string username, string password, string method, string uri)
        {
            string realm = authHeader.SIPDigest.Realm;
            string nonce = authHeader.SIPDigest.Nonce;
            string qop = authHeader.SIPDigest.Qop;

            string ha1 = $"{username}:{realm}:{password}".ToMD5Hash();
            Console.WriteLine($"HA1: {ha1}");
            string ha2 = $"{method}:{uri}".ToMD5Hash();
            Console.WriteLine($"HA2: {ha2}");

            string response;
            if (!string.IsNullOrEmpty(qop))
            {
                string cnonce = Guid.NewGuid().ToString("N").Substring(0, 8);
                string nc = "00000001";
                string responseInput = $"{ha1}:{nonce}:{nc}:{cnonce}:{qop}:{ha2}";
                Console.WriteLine($"Response input: {responseInput}");
                response = responseInput.ToMD5Hash();
                Console.WriteLine($"Response: {response}");
                return $"Digest username=\"{username}\" ,realm=\"{realm}\" ,nonce=\"{nonce}\" ,uri=\"{uri}\" ,response=\"{response}\" ,algorithm=MD5 ,qop={qop} ,cnonce=\"{cnonce}\" ,nc={nc}";
            }
            else
            {
                response = $"{ha1}:{nonce}:{ha2}".ToMD5Hash();
                return $"Digest username=\"{username}\",realm=\"{realm}\",nonce=\"{nonce}\",uri=\"{uri}\",response=\"{response}\",algorithm=MD5";
            }
        }
    }

    public static class StringExtensions
    {
        public static string ToMD5Hash(this string input)
        {
            using (var md5 = System.Security.Cryptography.MD5.Create())
            {
                byte[] inputBytes = System.Text.Encoding.UTF8.GetBytes(input);
                byte[] hashBytes = md5.ComputeHash(inputBytes);
                return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
            }
        }
    }
}
#endregion

#region udp信令 ffmpeg-udp推流livegbs正常,easygbs播不了
//using System;
//using System.Diagnostics;
//using System.Net;
//using System.Text;
//using SIPSorcery.SIP;
//using Timer = System.Timers.Timer;

//namespace GB28181Client
//{
//    class Program
//    {
//        private static SIPTransport _sipTransport;
//        private static Process _ffmpegProcess;
//        private static Timer _keepAliveTimer; // 添加定时器用于心跳
//        private static int _keepAliveInterval = 30000; // 默认心跳间隔30秒（单位：毫秒）
//        private static int _keepAliveSeq = 1; // 心跳序列号
//        private static string ssrc = "";


//        //private static string _deviceId = "34020000001320000002"; // 设备 ID
//        //private static string _serverIp = "192.168.10.138"; // 服务端 IP
//        //private static int _serverPort = 15060; // 服务端端口
//        //private static string _serverId = "34020000002000000001"; // 服务器Id
//        //private static string _serverpassword = "12345678"; // 服务器密码

//        //private static string _localIp = "192.168.10.138"; // 设备本地 IP
//        //private static int _localRtpPort = 20002; // 本地 RTP 端口
//        //private static string _mp4FilePath = Path.Combine(AppContext.BaseDirectory, "Temp", "EasyDarwin.mp4"); // MP4 文件路径


//        private static string _deviceId = "34020000001320000002"; // 设备 ID
//        private static string _serverIp = "10.168.1.128"; // 服务端 IP
//        private static int _serverPort = 15060; // 服务端端口
//        private static string _serverId = "34020000002000000001"; // 服务器Id
//        private static string _serverpassword = "ldsw1234"; // 服务器密码

//        private static string _localIp = "10.168.1.30"; // 设备本地 IP
//        private static int _localRtpPort = 20002; // 本地 RTP 端口
//        private static string _mp4FilePath = Path.Combine(AppContext.BaseDirectory, "Temp", "EasyDarwin.mp4");

//        static async Task Main(string[] args)
//        {
//            Console.WriteLine("Starting GB28181 Client...");

//            // 初始化 SIP 传输层
//            _sipTransport = new SIPTransport();
//            _sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.Any, 0))); // 随机本地端口

//            // 监听所有 SIP 请求和响应
//            _sipTransport.SIPTransportRequestReceived += OnRequestReceived;
//            _sipTransport.SIPTransportResponseReceived += OnResponseReceived;

//            // 发送 REGISTER 请求
//            await SendRegisterRequest(false); // 第一次不带认证头

//            // 初始化并启动心跳保活
//            StartKeepAlive(_keepAliveInterval);

//            Console.WriteLine("Press any key to exit...");
//            Console.ReadLine();

//            // 清理资源
//            StopKeepAlive();
//            _sipTransport.Shutdown();
//        }

//        // 启动心跳保活
//        private static void StartKeepAlive(int interval)
//        {
//            _keepAliveTimer = new Timer(interval);
//            _keepAliveTimer.Elapsed += async (sender, e) => await SendKeepAlive();
//            _keepAliveTimer.AutoReset = true;
//            _keepAliveTimer.Start();
//            Console.WriteLine($"KeepAlive started with interval: {interval}ms");
//        }

//        // 停止心跳保活
//        private static void StopKeepAlive()
//        {
//            if (_keepAliveTimer != null)
//            {
//                _keepAliveTimer.Stop();
//                _keepAliveTimer.Dispose();
//                Console.WriteLine("KeepAlive stopped");
//            }
//        }

//        // 发送心跳保活消息
//        private static async Task SendKeepAlive()
//        {
//            var localEndPoint = _sipTransport.GetSIPChannels()[0].ListeningSIPEndPoint;
//            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
//            var keepAliveRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

//            keepAliveRequest.Header = new SIPHeader();
//            keepAliveRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
//            keepAliveRequest.Header.To = new SIPToHeader(null, sipUri, null);
//            keepAliveRequest.Header.CallId = CallProperties.CreateNewCallId();
//            keepAliveRequest.Header.CSeq = _keepAliveSeq++;
//            keepAliveRequest.Header.MaxForwards = 70;
//            keepAliveRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
//            keepAliveRequest.Header.ContentType = "Application/MANSCDP+xml";
//            keepAliveRequest.Header.Vias.Via = new List<SIPViaHeader>
//            {
//                new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
//            };

//            string keepAliveXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Notify>
//    <CmdType>Keepalive</CmdType>
//    <SN>{_keepAliveSeq}</SN>
//    <DeviceID>{_deviceId}</DeviceID>
//    <Status>OK</Status>
//</Notify>";

//            keepAliveRequest.Body = keepAliveXml;
//            keepAliveRequest.Header.ContentLength = Encoding.UTF8.GetBytes(keepAliveXml).Length;

//            var remoteEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(IPAddress.Parse(_serverIp), _serverPort));
//            Console.WriteLine($"Sending KeepAlive to {remoteEndPoint}");
//            await _sipTransport.SendRequestAsync(remoteEndPoint, keepAliveRequest);
//        }

//        // 处理接收到的SIP请求
//        private static async Task OnRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            Console.WriteLine($"Received request: {request.Method} from {remoteEndPoint}");

//            if (request.Method == SIPMethodsEnum.MESSAGE)
//            {
//                // 检查是否为DeviceInfo查询
//                string content = request.Body;
//                if (content.Contains("<CmdType>DeviceInfo</CmdType>"))
//                {
//                    // 第一次响应：发送200 OK
//                    await SendOkResponse(localSIPEndPoint, remoteEndPoint, request);

//                    //Task.Run(() => SendDeviceInfoResponse(localSIPEndPoint, remoteEndPoint, request));
//                    await SendDeviceInfoResponse(localSIPEndPoint, remoteEndPoint, request);
//                }
//                else if (content.Contains("<CmdType>DeviceStatus</CmdType>"))  // 新增 DeviceStatus 处理
//                {
//                    await SendOkResponse(localSIPEndPoint, remoteEndPoint, request);
//                    await SendDeviceStatusResponse(localSIPEndPoint, remoteEndPoint, request);
//                }
//                else if (content.Contains("<CmdType>Catalog</CmdType>")) // 添加 Catalog 处理
//                {
//                    await SendOkResponse(localSIPEndPoint, remoteEndPoint, request);
//                    await SendCatalogResponse(localSIPEndPoint, remoteEndPoint, request);
//                }
//                else if (content.Contains("<CmdType>RecordInfo</CmdType>"))
//                {
//                    await SendOkResponse(localSIPEndPoint, remoteEndPoint, request);
//                    await SendRecordInfoResponse(localSIPEndPoint, remoteEndPoint, request);
//                }
//            }
//            else if (request.Method == SIPMethodsEnum.SUBSCRIBE) // 新增 SUBSCRIBE 处理
//            {
//                string content = request.Body;
//                if (content.Contains("<CmdType>Alarm</CmdType>"))
//                {
//                    await SendAlarmSubscriptionResponse(localSIPEndPoint, remoteEndPoint, request);
//                }
//                else if (content.Contains("<CmdType>MobilePosition</CmdType>")) // 新增移动位置订阅
//                {
//                    await SendMobilePositionSubscriptionResponse(localSIPEndPoint, remoteEndPoint, request);
//                }
//                else if (content.Contains("<CmdType>Catalog</CmdType>")) // 新增目录订阅
//                {
//                    await SendCatalogSubscriptionResponse(localSIPEndPoint, remoteEndPoint, request);
//                }
//            }
//            else if (request.Method == SIPMethodsEnum.INVITE)
//            {
//                // 1. 发送 100 Trying
//                await SendTryingResponse(localSIPEndPoint, remoteEndPoint, request);

//                // 2. 发送 200 OK 并提供 SDP
//                await SendInviteOkResponse(localSIPEndPoint, remoteEndPoint, request);

//                // 3. 启动 FFmpeg 推流
//                StartFfmpegPushStream(request);
//            }
//            else if (request.Method == SIPMethodsEnum.ACK)
//            {
//                Console.WriteLine("Received ACK, session established.");
//            }



//            await Task.CompletedTask;
//        }

//        // 发送200 OK响应
//        private static async Task SendOkResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            SIPResponse okResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Ok, "OK");
//            okResponse.Header.To.ToTag = CallProperties.CreateNewTag();
//            okResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
//            okResponse.Header.ContentLength = 0;

//            Console.WriteLine($"Sending 200 OK response to {remoteEndPoint}");
//            await _sipTransport.SendResponseAsync(okResponse);
//        }

//        // 发送DeviceInfo响应
//        private static async Task SendDeviceInfoResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
//            var deviceInfoRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

//            deviceInfoRequest.Header = new SIPHeader();
//            deviceInfoRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
//            deviceInfoRequest.Header.To = new SIPToHeader(null, sipUri, null);
//            deviceInfoRequest.Header.CallId = CallProperties.CreateNewCallId();
//            deviceInfoRequest.Header.CSeq = 1;
//            deviceInfoRequest.Header.MaxForwards = 70;
//            deviceInfoRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
//            deviceInfoRequest.Header.ContentType = "Application/MANSCDP+xml";
//            deviceInfoRequest.Header.Vias.Via = new List<SIPViaHeader>
//            {
//                new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
//            };

//            string sn = GetSNFromRequest(request.Body);
//            string deviceInfoXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Response>
//    <CmdType>DeviceInfo</CmdType>
//    <SN>{sn}</SN>
//    <DeviceID>{_deviceId}</DeviceID>
//    <DeviceName>EasyGBD-069600</DeviceName>
//    <Result>OK</Result>
//    <Manufacturer>hhh</Manufacturer>
//    <Model>EasyGBD</Model>
//    <Firmware>V2.0</Firmware>
//    <Channel>1</Channel>
//</Response>";

//            deviceInfoRequest.Body = deviceInfoXml;
//            deviceInfoRequest.Header.ContentLength = Encoding.UTF8.GetBytes(deviceInfoXml).Length;

//            Console.WriteLine($"Sending DeviceInfo response to {remoteEndPoint}");
//            await _sipTransport.SendRequestAsync(remoteEndPoint, deviceInfoRequest);
//        }

//        private static async Task SendDeviceStatusResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
//            var deviceStatusRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

//            deviceStatusRequest.Header = new SIPHeader();
//            deviceStatusRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
//            deviceStatusRequest.Header.To = new SIPToHeader(null, sipUri, null);
//            deviceStatusRequest.Header.CallId = CallProperties.CreateNewCallId();
//            deviceStatusRequest.Header.CSeq = 1;
//            deviceStatusRequest.Header.MaxForwards = 70;
//            deviceStatusRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
//            deviceStatusRequest.Header.ContentType = "Application/MANSCDP+xml";
//            deviceStatusRequest.Header.Vias.Via = new List<SIPViaHeader>
//    {
//        new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
//    };

//            string sn = GetSNFromRequest(request.Body);
//            string currentTime = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss");
//            string deviceStatusXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Response>
//    <CmdType>DeviceStatus</CmdType>
//    <SN>{sn}</SN>
//    <DeviceID>{_deviceId}</DeviceID>
//    <Result>OK</Result>
//    <Online>ONLINE</Online>
//    <Status>OK</Status>
//    <Encode>ON</Encode>
//    <Record>ON</Record>
//    <DeviceTime>{currentTime}</DeviceTime>
//    <Alarmstatus Num=""0"">
//    </Alarmstatus>
//</Response>";

//            deviceStatusRequest.Body = deviceStatusXml;
//            deviceStatusRequest.Header.ContentLength = Encoding.UTF8.GetBytes(deviceStatusXml).Length;

//            Console.WriteLine($"Sending DeviceStatus response to {remoteEndPoint}");
//            await _sipTransport.SendRequestAsync(remoteEndPoint, deviceStatusRequest);
//        }

//        // 新增 Catalog 响应方法
//        private static async Task SendCatalogResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
//            var catalogRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

//            catalogRequest.Header = new SIPHeader();
//            catalogRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
//            catalogRequest.Header.To = new SIPToHeader(null, sipUri, null);
//            catalogRequest.Header.CallId = CallProperties.CreateNewCallId();
//            catalogRequest.Header.CSeq = 1;
//            catalogRequest.Header.MaxForwards = 70;
//            catalogRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
//            catalogRequest.Header.ContentType = "Application/MANSCDP+xml";
//            catalogRequest.Header.Vias.Via = new List<SIPViaHeader>
//            {
//                new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
//            };

//            string sn = GetSNFromRequest(request.Body);
//            string catalogXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Response>
//    <CmdType>Catalog</CmdType>
//    <SN>{sn}</SN>
//    <DeviceID>{_deviceId}</DeviceID>
//    <SumNum>1</SumNum>
//    <DeviceList Num=""1"">
//        <Item>
//            <DeviceID>34020000001310000001</DeviceID>
//            <Name>EasyIPC</Name>
//            <Manufacturer>TSINGSEE</Manufacturer>
//            <Model>EasyGBD</Model>
//            <Owner>Owner</Owner>
//            <CivilCode>3402000000</CivilCode>
//            <Address>Address</Address>
//            <Parental>0</Parental>
//            <ParentID>{_deviceId}</ParentID>
//            <RegisterWay>1</RegisterWay>
//            <Secrecy>0</Secrecy>
//            <Status>ON</Status>
//        </Item>
//    </DeviceList>
//</Response>";

//            catalogRequest.Body = catalogXml;
//            catalogRequest.Header.ContentLength = Encoding.UTF8.GetBytes(catalogXml).Length;

//            Console.WriteLine($"Sending Catalog response to {remoteEndPoint}");
//            await _sipTransport.SendRequestAsync(remoteEndPoint, catalogRequest);
//        }

//        //告警订阅
//        private static async Task SendAlarmSubscriptionResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            // 创建 200 OK 响应
//            SIPResponse okResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Ok, "OK");
//            okResponse.Header.To.ToTag = CallProperties.CreateNewTag(); // 添加 To 标签
//            okResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
//            okResponse.Header.ContentType = "Application/MANSCDP+xml";
//            okResponse.Header.Event = "presence"; // 与请求中的 Event 一致
//            okResponse.Header.Expires = 0; // 与请求中的 Expires 一致

//            // 提取请求中的 SN
//            string sn = GetSNFromRequest(request.Body);

//            // 构造告警订阅的 XML 响应
//            string alarmXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Response>
//    <CmdType>Alarm</CmdType>
//    <SN>{sn}</SN>
//    <DeviceID>{_deviceId}</DeviceID>
//    <Result>OK</Result>
//</Response>";

//            // 设置响应正文和长度
//            okResponse.Body = alarmXml;
//            okResponse.Header.ContentLength = Encoding.UTF8.GetBytes(alarmXml).Length;

//            // 发送响应
//            Console.WriteLine($"Sending Alarm subscription 200 OK response to {remoteEndPoint}");
//            await _sipTransport.SendResponseAsync(okResponse);
//        }

//        //设备控制
//        private static async Task SendDeviceControlResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
//            var controlResponse = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

//            controlResponse.Header = new SIPHeader();
//            controlResponse.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
//            controlResponse.Header.To = new SIPToHeader(null, sipUri, null);
//            controlResponse.Header.CallId = CallProperties.CreateNewCallId();
//            controlResponse.Header.CSeq = 1;
//            controlResponse.Header.MaxForwards = 70;
//            controlResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
//            controlResponse.Header.ContentType = "Application/MANSCDP+xml";
//            controlResponse.Header.Vias.Via = new List<SIPViaHeader>
//    {
//        new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
//    };

//            string sn = GetSNFromRequest(request.Body);
//            string controlXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Response>
//    <CmdType>DeviceControl</CmdType>
//    <SN>{sn}</SN>
//    <DeviceID>{_deviceId}</DeviceID>
//    <Result>OK</Result>
//</Response>";

//            controlResponse.Body = controlXml;
//            controlResponse.Header.ContentLength = Encoding.UTF8.GetBytes(controlXml).Length;

//            Console.WriteLine($"Sending DeviceControl response to {remoteEndPoint}");
//            await _sipTransport.SendRequestAsync(remoteEndPoint, controlResponse);
//        }

//        //目标移动
//        private static async Task SendMobilePositionSubscriptionResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            SIPResponse okResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Ok, "OK");
//            okResponse.Header.To.ToTag = CallProperties.CreateNewTag();
//            okResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
//            okResponse.Header.ContentType = "Application/MANSCDP+xml";
//            okResponse.Header.Event = "presence";
//            okResponse.Header.Expires = request.Header.Expires;

//            string sn = GetSNFromRequest(request.Body);
//            string mobileXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Response>
//    <CmdType>MobilePosition</CmdType>
//    <SN>{sn}</SN>
//    <DeviceID>{_deviceId}</DeviceID>
//    <Result>OK</Result>
//</Response>";

//            okResponse.Body = mobileXml;
//            okResponse.Header.ContentLength = Encoding.UTF8.GetBytes(mobileXml).Length;

//            Console.WriteLine($"Sending MobilePosition subscription response to {remoteEndPoint}");
//            await _sipTransport.SendResponseAsync(okResponse);

//            // 如果需要定期发送位置数据，可以在这里启动定时器（见后续扩展）
//        }

//        //目录订阅
//        private static async Task SendCatalogSubscriptionResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            SIPResponse okResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Ok, "OK");
//            okResponse.Header.To.ToTag = CallProperties.CreateNewTag();
//            okResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
//            okResponse.Header.ContentType = "Application/MANSCDP+xml";
//            okResponse.Header.Event = request.Header.Event; // 保持与请求一致，如 "Catalog;id=41831755"
//            okResponse.Header.Expires = request.Header.Expires;
//            okResponse.Header.Contact = new List<SIPContactHeader>
//    {
//        new SIPContactHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{localEndPoint.Address}:{localEndPoint.Port}"))
//    };

//            string sn = GetSNFromRequest(request.Body);
//            string catalogXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Response>
//    <CmdType>Catalog</CmdType>
//    <SN>{sn}</SN>
//    <DeviceID>{_deviceId}</DeviceID>
//    <Result>OK</Result>
//</Response>";

//            okResponse.Body = catalogXml;
//            okResponse.Header.ContentLength = Encoding.UTF8.GetBytes(catalogXml).Length;

//            Console.WriteLine($"Sending Catalog subscription response to {remoteEndPoint}");
//            await _sipTransport.SendResponseAsync(okResponse);

//            // 如果需要定期发送目录数据，可以在这里启动定时器（见后续扩展）
//        }

//        //目录订阅
//        private static async Task SendRecordInfoResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            SIPResponse okResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Ok, "OK");
//            okResponse.Header.To.ToTag = CallProperties.CreateNewTag();
//            okResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
//            okResponse.Header.ContentType = "Application/MANSCDP+xml";
//            okResponse.Header.Event = request.Header.Event; // 保持与请求一致，如 "Catalog;id=41831755"
//            okResponse.Header.Expires = request.Header.Expires;
//            okResponse.Header.Contact = new List<SIPContactHeader>
//    {
//        new SIPContactHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{localEndPoint.Address}:{localEndPoint.Port}"))
//    };

//            string sn = GetSNFromRequest(request.Body);
//            string catalogXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Response>
//    <CmdType>RecordInfo</CmdType>
//    <SN>{sn}</SN>
//    <DeviceID>34020000001310000001</DeviceID>
//    <Name>EasyGBD-069600</Name>
//    <SumNum>2</SumNum>
//    <RecordList Num=""2"">
//    <Item>
//    <DeviceID>34020000001310000001</DeviceID>
//    <Name>EasyGBD-069600</Name>
//    <Address></Address>
//    <StartTime>2025-03-26T00:00:00</StartTime>
//    <EndTime>2025-03-26T00:10:00</EndTime>
//    <Secrecy>0</Secrecy>
//    <Type>all</Type>
//    <FileSize>10485760</FileSize>
//    </Item>
//    <Item>
//    <DeviceID>34020000001310000001</DeviceID>
//    <Name>EasyGBD-069600</Name>
//    <Address></Address>
//    <StartTime>2025-03-26T00:10:00</StartTime>
//    <EndTime>2025-03-26T00:20:00</EndTime>
//    <Secrecy>0</Secrecy>
//    <Type>all</Type>
//    <FileSize>10485760</FileSize>
//    </Item>
//    </RecordList>
//</Response>";

//            okResponse.Body = catalogXml;
//            okResponse.Header.ContentLength = Encoding.UTF8.GetBytes(catalogXml).Length;

//            Console.WriteLine($"Sending RecordInfo response to {remoteEndPoint}");
//            await _sipTransport.SendResponseAsync(okResponse);

//            // 如果需要定期发送目录数据，可以在这里启动定时器（见后续扩展）
//        }

//        //100 try
//        private static async Task SendTryingResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            SIPResponse tryingResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Trying, "Trying");
//            tryingResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
//            tryingResponse.Header.ContentLength = 0;

//            Console.WriteLine($"Sending 100 Trying to {remoteEndPoint}");
//            await _sipTransport.SendResponseAsync(tryingResponse);
//        }

//        //视频播放
//        private static async Task SendInviteOkResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            SIPResponse okResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Ok, "OK");
//            okResponse.Header.To.ToTag = CallProperties.CreateNewTag();
//            okResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
//            okResponse.Header.Contact = new List<SIPContactHeader>
//            {
//                new SIPContactHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_localIp}"))
//            };
//            okResponse.Header.ContentType = "application/sdp";

//            // 解析服务器 SDP
//            string[] sdpLines = request.Body.Split('\n');
//            string serverIp = sdpLines.FirstOrDefault(l => l.StartsWith("c="))?.Split(' ').LastOrDefault()?.Trim() ?? _serverIp;
//            string mediaLine = sdpLines.FirstOrDefault(l => l.StartsWith("m=video"));
//            int serverRtpPort = int.Parse(mediaLine?.Split(' ')[1] ?? "30264");
//            ssrc = sdpLines.FirstOrDefault(l => l.StartsWith("y="))?.Substring(2).Replace("\r", ""); // 获取 INVITE 的 SSRC

//            var time = (request.Body.Split('\n').FirstOrDefault(l => l.StartsWith("t="))?.Trim() ?? "0 0").Replace("t=", "");

//            Console.WriteLine($"Received INVITE SDP:\n{request.Body}");

//            //m=video {_localRtpPort} TCP/RTP/AVP 96

//            // 生成设备的 SDP，使用 RTP/AVP
//            string sdp = $@"v=0
//o={_deviceId} 0 0 IN IP4 {_localIp}
//s=Play
//c=IN IP4 {_localIp}
//t={time}
//m=video {_localRtpPort} RTP/AVP 98
//a=rtpmap:98 H264/90000
//a=sendonly
//y={ssrc}
//f=v/2////a///";

//            okResponse.Body = sdp;
//            okResponse.Header.ContentLength = Encoding.UTF8.GetBytes(sdp).Length;

//            Console.WriteLine($"Sending 200 OK with SDP to {remoteEndPoint}");
//            Console.WriteLine($"SDP Content:\n{sdp}");
//            await _sipTransport.SendResponseAsync(okResponse);
//        }

//        //ffmpeg推流
//        private static void StartFfmpegPushStream(SIPRequest request)
//        {
//            string[] sdpLines = request.Body.Split('\n');
//            string serverIp = sdpLines.FirstOrDefault(l => l.StartsWith("c="))?.Split(' ').LastOrDefault()?.Trim() ?? _serverIp;
//            string mediaLine = sdpLines.FirstOrDefault(l => l.StartsWith("m=video"));
//            int serverRtpPort = int.Parse(mediaLine?.Split(' ')[1] ?? "30264"); // 从 INVITE SDP 获取服务器端口

//            Console.WriteLine($"Parsed SDP: Server IP = {serverIp}, Port = {serverRtpPort}");

//            // 使用 _localRtpPort 作为源端口，推送至服务器端口
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -f mpegts -local_rtpport {_localRtpPort} \"udp://{serverIp}:{serverRtpPort}\"";
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -f mpegts -local_rtpport {_localRtpPort} \"udp://{serverIp}:{serverRtpPort}\"";
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -f rtsp -an \"rtsp://{serverIp}:{serverRtpPort}/stream\"";
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v libx264 -b:v 2000k -r 25 -f mpegts -an -loglevel verbose \"udp://{serverIp}:{serverRtpPort}\"";
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v libx264 -b:v 2000k -r 25 -c:a aac -b:a 128k -f rtp -payload_type 96 -ssrc {ssrc} \"rtp://{serverIp}:{serverRtpPort}\"";
//            string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v libx264 -b:v 2000k -r 25 -profile:v baseline -level 3.0 -an -f rtp -payload_type 98 -ssrc {ssrc} \"rtp://{serverIp}:{serverRtpPort}\"";


//            Console.WriteLine($"Starting FFmpeg push stream: ffmpeg {ffmpegArgs}");

//            ProcessStartInfo psi = new ProcessStartInfo
//            {
//                FileName = "ffmpeg",
//                Arguments = ffmpegArgs,
//                RedirectStandardOutput = true,
//                RedirectStandardError = true,
//                UseShellExecute = false,
//                CreateNoWindow = true
//            };

//            try
//            {
//                _ffmpegProcess = new Process { StartInfo = psi };
//                _ffmpegProcess.EnableRaisingEvents = true;

//                _ffmpegProcess.OutputDataReceived += (sender, e) =>
//                {
//                    if (e.Data != null)
//                        Console.WriteLine($"FFmpeg Output: {e.Data}");
//                };
//                _ffmpegProcess.ErrorDataReceived += (sender, e) =>
//                {
//                    if (e.Data != null)
//                    {
//                        // 检查是否包含错误关键词
//                        if (e.Data.Contains("Error") || e.Data.Contains("failed") || e.Data.Contains("Invalid"))
//                        {
//                            Console.WriteLine($"FFmpeg Error: {e.Data}");
//                        }
//                        else
//                        {
//                            Console.WriteLine($"FFmpeg Info: {e.Data}");
//                        }
//                    }
//                };
//                _ffmpegProcess.Exited += (sender, e) =>
//                {
//                    Console.WriteLine($"FFmpeg process exited with code: {_ffmpegProcess.ExitCode}");
//                };

//                bool started = _ffmpegProcess.Start();
//                if (started)
//                {
//                    Console.WriteLine("FFmpeg process started successfully.");
//                    _ffmpegProcess.BeginOutputReadLine();
//                    _ffmpegProcess.BeginErrorReadLine();
//                }
//                else
//                {
//                    Console.WriteLine("Failed to start FFmpeg process.");
//                }

//                Task.Run(async () =>
//                {
//                    await Task.Run(() => _ffmpegProcess.WaitForExit());
//                    Console.WriteLine($"FFmpeg process completed with exit code: {_ffmpegProcess.ExitCode}");
//                });
//            }
//            catch (Exception ex)
//            {
//                Console.WriteLine($"Error starting FFmpeg: {ex.Message}");
//            }
//        }

//        // 从请求中提取SN
//        private static string GetSNFromRequest(string requestBody)
//        {
//            try
//            {
//                int snStart = requestBody.IndexOf("<SN>") + 4;
//                int snEnd = requestBody.IndexOf("</SN>");
//                return requestBody.Substring(snStart, snEnd - snStart);
//            }
//            catch
//            {
//                return "0";
//            }
//        }

//        // 发送 REGISTER 请求
//        private static async Task SendRegisterRequest(bool withAuth, string authHeader = null)
//        {
//            var sipUri = SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}");
//            var regRequest = new SIPRequest(SIPMethodsEnum.REGISTER, sipUri);

//            regRequest.Header = new SIPHeader();

//            // 获取本地端点
//            var localEndPoint = _sipTransport.GetSIPChannels()[0].ListeningSIPEndPoint;
//            if (localEndPoint == null)
//            {
//                Console.WriteLine("Local SIP endpoint is null!");
//                return;
//            }

//            // 设置 From 和 To 头
//            regRequest.Header.From = new SIPFromHeader(null, sipUri, null);
//            regRequest.Header.To = new SIPToHeader(null, sipUri, null);
//            regRequest.Header.Contact = new List<SIPContactHeader>
//            {
//                new SIPContactHeader(null, new SIPURI(SIPSchemesEnum.sip, localEndPoint.Address, localEndPoint.Port) { User = _deviceId })
//            };
//            regRequest.Header.Vias.Via = new List<SIPViaHeader>
//            {
//                new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
//            };
//            regRequest.Header.CSeq = withAuth ? 2 : 1;
//            regRequest.Header.CallId = Guid.NewGuid().ToString();
//            regRequest.Header.Expires = 3600;

//            // 如果需要认证，设置 Authorization 头
//            if (withAuth && authHeader != null)
//            {
//                regRequest.Header.AuthenticationHeaders = new List<SIPAuthenticationHeader>
//                {
//                    SIPAuthenticationHeader.ParseSIPAuthenticationHeader(SIPAuthorisationHeadersEnum.Authorize, authHeader)
//                };
//                Console.WriteLine($"Added Authorization header: {authHeader}");
//            }

//            // 指定目标端点并发送请求
//            var dstEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(IPAddress.Parse(_serverIp), _serverPort));
//            Console.WriteLine("Sending REGISTER request to: " + dstEndPoint);
//            await _sipTransport.SendRequestAsync(dstEndPoint, regRequest);
//        }

//        // 处理服务端返回的响应
//        private static Task OnResponseReceived(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPResponse response)
//        {
//            Console.WriteLine($"Received response: {response.StatusCode} {response.ReasonPhrase}");

//            if (response.Status == SIPResponseStatusCodesEnum.Unauthorised)
//            {
//                if (response.Header.AuthenticationHeaders != null && response.Header.AuthenticationHeaders.Count > 0)
//                {
//                    var wwwAuthHeader = response.Header.AuthenticationHeaders[0];
//                    if (wwwAuthHeader != null)
//                    {
//                        string authHeader = ComputeDigestAuth(wwwAuthHeader, _deviceId, _serverpassword, "REGISTER", $"sip:{_serverId}@{wwwAuthHeader.SIPDigest.Realm}");
//                        Console.WriteLine($"Generated Authorization: {authHeader}");
//                        Task.Run(() => SendRegisterRequest(true, authHeader));
//                    }
//                }
//                else
//                {
//                    Console.WriteLine("No WWW-Authenticate header found in 401 response.");
//                }
//            }
//            else if (response.Status == SIPResponseStatusCodesEnum.Ok)
//            {
//                Console.WriteLine("服务器响应ok");
//            }
//            else
//            {
//                Console.WriteLine($"Unexpected response: {response.Status}");
//            }

//            return Task.CompletedTask;
//        }

//        //md5加密
//        private static string ComputeDigestAuth(SIPAuthenticationHeader authHeader, string username, string password, string method, string uri)
//        {
//            string realm = authHeader.SIPDigest.Realm;
//            string nonce = authHeader.SIPDigest.Nonce;
//            string qop = authHeader.SIPDigest.Qop;

//            string ha1 = $"{username}:{realm}:{password}".ToMD5Hash();
//            Console.WriteLine($"HA1: {ha1}");
//            string ha2 = $"{method}:{uri}".ToMD5Hash();
//            Console.WriteLine($"HA2: {ha2}");

//            string response;
//            if (!string.IsNullOrEmpty(qop))
//            {
//                string cnonce = Guid.NewGuid().ToString("N").Substring(0, 8);
//                string nc = "00000001";
//                string responseInput = $"{ha1}:{nonce}:{nc}:{cnonce}:{qop}:{ha2}";
//                Console.WriteLine($"Response input: {responseInput}");
//                response = responseInput.ToMD5Hash();
//                Console.WriteLine($"Response: {response}");
//                return $"Digest username=\"{username}\" ,realm=\"{realm}\" ,nonce=\"{nonce}\" ,uri=\"{uri}\" ,response=\"{response}\" ,algorithm=MD5 ,qop={qop} ,cnonce=\"{cnonce}\" ,nc={nc}";
//            }
//            else
//            {
//                response = $"{ha1}:{nonce}:{ha2}".ToMD5Hash();
//                return $"Digest username=\"{username}\",realm=\"{realm}\",nonce=\"{nonce}\",uri=\"{uri}\",response=\"{response}\",algorithm=MD5";
//            }
//        }
//    }

//    public static class StringExtensions
//    {
//        public static string ToMD5Hash(this string input)
//        {
//            using (var md5 = System.Security.Cryptography.MD5.Create())
//            {
//                byte[] inputBytes = System.Text.Encoding.UTF8.GetBytes(input);
//                byte[] hashBytes = md5.ComputeHash(inputBytes);
//                return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
//            }
//        }
//    }
//} 
#endregion

#region ffmpeg rtp over tcp主动推流本地livegbs花屏，卡顿 使用abl推流正常--暂时不知道什么原因
//using System;
//using System.Diagnostics;
//using System.Net;
//using System.Text;
//using SIPSorcery.SIP;
//using Timer = System.Timers.Timer;

//namespace GB28181Client
//{
//    class Program
//    {
//        private static SIPTransport _sipTransport;
//        private static Process _ffmpegProcess;
//        private static Timer _keepAliveTimer; // 添加定时器用于心跳
//        private static int _keepAliveInterval = 30000; // 默认心跳间隔30秒（单位：毫秒）
//        private static int _keepAliveSeq = 1; // 心跳序列号
//        private static string ssrc = "";


//        //private static string _deviceId = "34020000001320000002"; // 设备 ID
//        //private static string _serverIp = "212.64.34.165"; // 服务端 IP
//        //private static int _serverPort = 15060; // 服务端端口
//        //private static string _serverId = "34020000002000000001"; // 服务器Id
//        //private static string _serverpassword = "12345678"; // 服务器密码

//        //private static string _localIp = "192.168.10.138"; // 设备本地 IP
//        //private static int _localRtpPort = 20002; // 本地 RTP 端口  
//        ////private static string _mp4FilePath = Path.Combine(AppContext.BaseDirectory, "Temp", "EasyDarwin.mp4"); //"C:\\Users\\ldsw_\\Downloads\\EasyGBD-Win-24.1225\\EasyDarwin.mp4"; // MP4 文件路径
//        //private static string _mp4FilePath = @"C:\Users\ldsw_\Downloads\EasyGBD-Win-24.1225\EasyDarwin.mp4";


//        private static string _deviceId = "34020000001320000002"; // 设备 ID
//        private static string _serverIp = "10.168.1.128"; // 服务端 IP
//        private static int _serverPort = 15060; // 服务端端口
//        private static string _serverId = "34020000002000000001"; // 服务器Id
//        private static string _serverpassword = "ldsw1234"; // 服务器密码

//        private static string _localIp = "10.168.1.30"; // 设备本地 IP
//        private static int _localRtpPort = 20002; // 本地 RTP 端口
//        private static string _mp4FilePath = @"C:\Users\ldsw_\Downloads\EasyGBD-Win-24.1225\EasyDarwin.mp4";

//        static async Task Main(string[] args)
//        {
//            Console.WriteLine("Starting GB28181 Client...");

//            // 初始化 SIP 传输层
//            _sipTransport = new SIPTransport();
//            _sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.Any, 0))); // 随机本地端口

//            // 监听所有 SIP 请求和响应
//            _sipTransport.SIPTransportRequestReceived += OnRequestReceived;
//            _sipTransport.SIPTransportResponseReceived += OnResponseReceived;

//            // 发送 REGISTER 请求
//            await SendRegisterRequest(false); // 第一次不带认证头

//            // 初始化并启动心跳保活
//            StartKeepAlive(_keepAliveInterval);

//            Console.WriteLine("Press any key to exit...");
//            Console.ReadLine();

//            // 清理资源
//            StopKeepAlive();
//            _sipTransport.Shutdown();
//        }

//        // 启动心跳保活
//        private static void StartKeepAlive(int interval)
//        {
//            _keepAliveTimer = new Timer(interval);
//            _keepAliveTimer.Elapsed += async (sender, e) => await SendKeepAlive();
//            _keepAliveTimer.AutoReset = true;
//            _keepAliveTimer.Start();
//            Console.WriteLine($"KeepAlive started with interval: {interval}ms");
//        }

//        // 停止心跳保活
//        private static void StopKeepAlive()
//        {
//            if (_keepAliveTimer != null)
//            {
//                _keepAliveTimer.Stop();
//                _keepAliveTimer.Dispose();
//                Console.WriteLine("KeepAlive stopped");
//            }
//        }

//        // 发送心跳保活消息
//        private static async Task SendKeepAlive()
//        {
//            var localEndPoint = _sipTransport.GetSIPChannels()[0].ListeningSIPEndPoint;
//            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
//            var keepAliveRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

//            keepAliveRequest.Header = new SIPHeader();
//            keepAliveRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
//            keepAliveRequest.Header.To = new SIPToHeader(null, sipUri, null);
//            keepAliveRequest.Header.CallId = CallProperties.CreateNewCallId();
//            keepAliveRequest.Header.CSeq = _keepAliveSeq++;
//            keepAliveRequest.Header.MaxForwards = 70;
//            keepAliveRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
//            keepAliveRequest.Header.ContentType = "Application/MANSCDP+xml";
//            keepAliveRequest.Header.Vias.Via = new List<SIPViaHeader>
//            {
//                new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
//            };

//            string keepAliveXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Notify>
//    <CmdType>Keepalive</CmdType>
//    <SN>{_keepAliveSeq}</SN>
//    <DeviceID>{_deviceId}</DeviceID>
//    <Status>OK</Status>
//</Notify>";

//            keepAliveRequest.Body = keepAliveXml;
//            keepAliveRequest.Header.ContentLength = Encoding.UTF8.GetBytes(keepAliveXml).Length;

//            var remoteEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(IPAddress.Parse(_serverIp), _serverPort));
//            Console.WriteLine($"Sending KeepAlive to {remoteEndPoint}");
//            await _sipTransport.SendRequestAsync(remoteEndPoint, keepAliveRequest);
//        }

//        // 处理接收到的SIP请求
//        private static async Task OnRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            Console.WriteLine($"Received request: {request.Method} from {remoteEndPoint}");

//            if (request.Method == SIPMethodsEnum.MESSAGE)
//            {
//                // 检查是否为DeviceInfo查询
//                string content = request.Body;
//                if (content.Contains("<CmdType>DeviceInfo</CmdType>"))
//                {
//                    // 第一次响应：发送200 OK
//                    await SendOkResponse(localSIPEndPoint, remoteEndPoint, request);

//                    //Task.Run(() => SendDeviceInfoResponse(localSIPEndPoint, remoteEndPoint, request));
//                    await SendDeviceInfoResponse(localSIPEndPoint, remoteEndPoint, request);
//                }
//                else if (content.Contains("<CmdType>DeviceStatus</CmdType>"))  // 新增 DeviceStatus 处理
//                {
//                    await SendOkResponse(localSIPEndPoint, remoteEndPoint, request);
//                    await SendDeviceStatusResponse(localSIPEndPoint, remoteEndPoint, request);
//                }
//                else if (content.Contains("<CmdType>Catalog</CmdType>")) // 添加 Catalog 处理
//                {
//                    await SendOkResponse(localSIPEndPoint, remoteEndPoint, request);
//                    await SendCatalogResponse(localSIPEndPoint, remoteEndPoint, request);
//                }
//            }
//            else if (request.Method == SIPMethodsEnum.SUBSCRIBE) // 新增 SUBSCRIBE 处理
//            {
//                string content = request.Body;
//                if (content.Contains("<CmdType>Alarm</CmdType>"))
//                {
//                    await SendAlarmSubscriptionResponse(localSIPEndPoint, remoteEndPoint, request);
//                }
//                else if (content.Contains("<CmdType>MobilePosition</CmdType>")) // 新增移动位置订阅
//                {
//                    await SendMobilePositionSubscriptionResponse(localSIPEndPoint, remoteEndPoint, request);
//                }
//                else if (content.Contains("<CmdType>Catalog</CmdType>")) // 新增目录订阅
//                {
//                    await SendCatalogSubscriptionResponse(localSIPEndPoint, remoteEndPoint, request);
//                }
//            }
//            else if (request.Method == SIPMethodsEnum.INVITE)
//            {
//                // 1. 发送 100 Trying
//                await SendTryingResponse(localSIPEndPoint, remoteEndPoint, request);

//                // 2. 发送 200 OK 并提供 SDP
//                await SendInviteOkResponse(localSIPEndPoint, remoteEndPoint, request);

//                // 3. 启动 FFmpeg 推流
//                StartFfmpegPushStream(request);
//            }
//            else if (request.Method == SIPMethodsEnum.ACK)
//            {
//                Console.WriteLine("Received ACK, session established.");
//            }



//            await Task.CompletedTask;
//        }

//        // 发送200 OK响应
//        private static async Task SendOkResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            SIPResponse okResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Ok, "OK");
//            okResponse.Header.To.ToTag = CallProperties.CreateNewTag();
//            okResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
//            okResponse.Header.ContentLength = 0;

//            Console.WriteLine($"Sending 200 OK response to {remoteEndPoint}");
//            await _sipTransport.SendResponseAsync(okResponse);
//        }

//        // 发送DeviceInfo响应
//        private static async Task SendDeviceInfoResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
//            var deviceInfoRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

//            deviceInfoRequest.Header = new SIPHeader();
//            deviceInfoRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
//            deviceInfoRequest.Header.To = new SIPToHeader(null, sipUri, null);
//            deviceInfoRequest.Header.CallId = CallProperties.CreateNewCallId();
//            deviceInfoRequest.Header.CSeq = 1;
//            deviceInfoRequest.Header.MaxForwards = 70;
//            deviceInfoRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
//            deviceInfoRequest.Header.ContentType = "Application/MANSCDP+xml";
//            deviceInfoRequest.Header.Vias.Via = new List<SIPViaHeader>
//            {
//                new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
//            };

//            string sn = GetSNFromRequest(request.Body);
//            string deviceInfoXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Response>
//    <CmdType>DeviceInfo</CmdType>
//    <SN>{sn}</SN>
//    <DeviceID>{_deviceId}</DeviceID>
//    <DeviceName>EasyGBD-069600</DeviceName>
//    <Result>OK</Result>
//    <Manufacturer>hhh</Manufacturer>
//    <Model>EasyGBD</Model>
//    <Firmware>V2.0</Firmware>
//    <Channel>1</Channel>
//</Response>";

//            deviceInfoRequest.Body = deviceInfoXml;
//            deviceInfoRequest.Header.ContentLength = Encoding.UTF8.GetBytes(deviceInfoXml).Length;

//            Console.WriteLine($"Sending DeviceInfo response to {remoteEndPoint}");
//            await _sipTransport.SendRequestAsync(remoteEndPoint, deviceInfoRequest);
//        }

//        private static async Task SendDeviceStatusResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
//            var deviceStatusRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

//            deviceStatusRequest.Header = new SIPHeader();
//            deviceStatusRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
//            deviceStatusRequest.Header.To = new SIPToHeader(null, sipUri, null);
//            deviceStatusRequest.Header.CallId = CallProperties.CreateNewCallId();
//            deviceStatusRequest.Header.CSeq = 1;
//            deviceStatusRequest.Header.MaxForwards = 70;
//            deviceStatusRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
//            deviceStatusRequest.Header.ContentType = "Application/MANSCDP+xml";
//            deviceStatusRequest.Header.Vias.Via = new List<SIPViaHeader>
//    {
//        new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
//    };

//            string sn = GetSNFromRequest(request.Body);
//            string currentTime = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss");
//            string deviceStatusXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Response>
//    <CmdType>DeviceStatus</CmdType>
//    <SN>{sn}</SN>
//    <DeviceID>{_deviceId}</DeviceID>
//    <Result>OK</Result>
//    <Online>ONLINE</Online>
//    <Status>OK</Status>
//    <Encode>ON</Encode>
//    <Record>ON</Record>
//    <DeviceTime>{currentTime}</DeviceTime>
//    <Alarmstatus Num=""0"">
//    </Alarmstatus>
//</Response>";

//            deviceStatusRequest.Body = deviceStatusXml;
//            deviceStatusRequest.Header.ContentLength = Encoding.UTF8.GetBytes(deviceStatusXml).Length;

//            Console.WriteLine($"Sending DeviceStatus response to {remoteEndPoint}");
//            await _sipTransport.SendRequestAsync(remoteEndPoint, deviceStatusRequest);
//        }

//        // 新增 Catalog 响应方法
//        private static async Task SendCatalogResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
//            var catalogRequest = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

//            catalogRequest.Header = new SIPHeader();
//            catalogRequest.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
//            catalogRequest.Header.To = new SIPToHeader(null, sipUri, null);
//            catalogRequest.Header.CallId = CallProperties.CreateNewCallId();
//            catalogRequest.Header.CSeq = 1;
//            catalogRequest.Header.MaxForwards = 70;
//            catalogRequest.Header.UserAgent = "Easy GB28181 Device V7.0";
//            catalogRequest.Header.ContentType = "Application/MANSCDP+xml";
//            catalogRequest.Header.Vias.Via = new List<SIPViaHeader>
//            {
//                new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
//            };

//            string sn = GetSNFromRequest(request.Body);
//            string catalogXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Response>
//    <CmdType>Catalog</CmdType>
//    <SN>{sn}</SN>
//    <DeviceID>{_deviceId}</DeviceID>
//    <SumNum>1</SumNum>
//    <DeviceList Num=""1"">
//        <Item>
//            <DeviceID>34020000001310000001</DeviceID>
//            <Name>EasyIPC</Name>
//            <Manufacturer>TSINGSEE</Manufacturer>
//            <Model>EasyGBD</Model>
//            <Owner>Owner</Owner>
//            <CivilCode>3402000000</CivilCode>
//            <Address>Address</Address>
//            <Parental>0</Parental>
//            <ParentID>{_deviceId}</ParentID>
//            <RegisterWay>1</RegisterWay>
//            <Secrecy>0</Secrecy>
//            <Status>ON</Status>
//        </Item>
//    </DeviceList>
//</Response>";

//            catalogRequest.Body = catalogXml;
//            catalogRequest.Header.ContentLength = Encoding.UTF8.GetBytes(catalogXml).Length;

//            Console.WriteLine($"Sending Catalog response to {remoteEndPoint}");
//            await _sipTransport.SendRequestAsync(remoteEndPoint, catalogRequest);
//        }

//        //告警订阅
//        private static async Task SendAlarmSubscriptionResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            // 创建 200 OK 响应
//            SIPResponse okResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Ok, "OK");
//            okResponse.Header.To.ToTag = CallProperties.CreateNewTag(); // 添加 To 标签
//            okResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
//            okResponse.Header.ContentType = "Application/MANSCDP+xml";
//            okResponse.Header.Event = "presence"; // 与请求中的 Event 一致
//            okResponse.Header.Expires = 0; // 与请求中的 Expires 一致

//            // 提取请求中的 SN
//            string sn = GetSNFromRequest(request.Body);

//            // 构造告警订阅的 XML 响应
//            string alarmXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Response>
//    <CmdType>Alarm</CmdType>
//    <SN>{sn}</SN>
//    <DeviceID>{_deviceId}</DeviceID>
//    <Result>OK</Result>
//</Response>";

//            // 设置响应正文和长度
//            okResponse.Body = alarmXml;
//            okResponse.Header.ContentLength = Encoding.UTF8.GetBytes(alarmXml).Length;

//            // 发送响应
//            Console.WriteLine($"Sending Alarm subscription 200 OK response to {remoteEndPoint}");
//            await _sipTransport.SendResponseAsync(okResponse);
//        }

//        //设备控制
//        private static async Task SendDeviceControlResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            var sipUri = SIPURI.ParseSIPURI($"sip:{_serverId}@{_serverIp}:{_serverPort}");
//            var controlResponse = new SIPRequest(SIPMethodsEnum.MESSAGE, sipUri);

//            controlResponse.Header = new SIPHeader();
//            controlResponse.Header.From = new SIPFromHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}"), CallProperties.CreateNewTag());
//            controlResponse.Header.To = new SIPToHeader(null, sipUri, null);
//            controlResponse.Header.CallId = CallProperties.CreateNewCallId();
//            controlResponse.Header.CSeq = 1;
//            controlResponse.Header.MaxForwards = 70;
//            controlResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
//            controlResponse.Header.ContentType = "Application/MANSCDP+xml";
//            controlResponse.Header.Vias.Via = new List<SIPViaHeader>
//    {
//        new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
//    };

//            string sn = GetSNFromRequest(request.Body);
//            string controlXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Response>
//    <CmdType>DeviceControl</CmdType>
//    <SN>{sn}</SN>
//    <DeviceID>{_deviceId}</DeviceID>
//    <Result>OK</Result>
//</Response>";

//            controlResponse.Body = controlXml;
//            controlResponse.Header.ContentLength = Encoding.UTF8.GetBytes(controlXml).Length;

//            Console.WriteLine($"Sending DeviceControl response to {remoteEndPoint}");
//            await _sipTransport.SendRequestAsync(remoteEndPoint, controlResponse);
//        }

//        //目标移动
//        private static async Task SendMobilePositionSubscriptionResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            SIPResponse okResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Ok, "OK");
//            okResponse.Header.To.ToTag = CallProperties.CreateNewTag();
//            okResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
//            okResponse.Header.ContentType = "Application/MANSCDP+xml";
//            okResponse.Header.Event = "presence";
//            okResponse.Header.Expires = request.Header.Expires;

//            string sn = GetSNFromRequest(request.Body);
//            string mobileXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Response>
//    <CmdType>MobilePosition</CmdType>
//    <SN>{sn}</SN>
//    <DeviceID>{_deviceId}</DeviceID>
//    <Result>OK</Result>
//</Response>";

//            okResponse.Body = mobileXml;
//            okResponse.Header.ContentLength = Encoding.UTF8.GetBytes(mobileXml).Length;

//            Console.WriteLine($"Sending MobilePosition subscription response to {remoteEndPoint}");
//            await _sipTransport.SendResponseAsync(okResponse);

//            // 如果需要定期发送位置数据，可以在这里启动定时器（见后续扩展）
//        }

//        //目录订阅
//        private static async Task SendCatalogSubscriptionResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            SIPResponse okResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Ok, "OK");
//            okResponse.Header.To.ToTag = CallProperties.CreateNewTag();
//            okResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
//            okResponse.Header.ContentType = "Application/MANSCDP+xml";
//            okResponse.Header.Event = request.Header.Event; // 保持与请求一致，如 "Catalog;id=41831755"
//            okResponse.Header.Expires = request.Header.Expires;
//            okResponse.Header.Contact = new List<SIPContactHeader>
//    {
//        new SIPContactHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{localEndPoint.Address}:{localEndPoint.Port}"))
//    };

//            string sn = GetSNFromRequest(request.Body);
//            string catalogXml = $@"<?xml version=""1.0"" encoding=""GB2312""?>
//<Response>
//    <CmdType>Catalog</CmdType>
//    <SN>{sn}</SN>
//    <DeviceID>{_deviceId}</DeviceID>
//    <Result>OK</Result>
//</Response>";

//            okResponse.Body = catalogXml;
//            okResponse.Header.ContentLength = Encoding.UTF8.GetBytes(catalogXml).Length;

//            Console.WriteLine($"Sending Catalog subscription response to {remoteEndPoint}");
//            await _sipTransport.SendResponseAsync(okResponse);

//            // 如果需要定期发送目录数据，可以在这里启动定时器（见后续扩展）
//        }

//        //100 try
//        private static async Task SendTryingResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            SIPResponse tryingResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Trying, "Trying");
//            tryingResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
//            tryingResponse.Header.ContentLength = 0;

//            Console.WriteLine($"Sending 100 Trying to {remoteEndPoint}");
//            await _sipTransport.SendResponseAsync(tryingResponse);
//        }

//        //视频播放
//        private static async Task SendInviteOkResponse(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPRequest request)
//        {
//            SIPResponse okResponse = SIPResponse.GetResponse(request, SIPResponseStatusCodesEnum.Ok, "OK");
//            okResponse.Header.To.ToTag = CallProperties.CreateNewTag();
//            okResponse.Header.UserAgent = "Easy GB28181 Device V7.0";
//            okResponse.Header.Contact = new List<SIPContactHeader>
//            {
//                new SIPContactHeader(null, SIPURI.ParseSIPURI($"sip:{_deviceId}@{_localIp}"))
//            };
//            okResponse.Header.ContentType = "application/sdp";

//            // 解析服务器 SDP
//            string[] sdpLines = request.Body.Split('\n');
//            string serverIp = sdpLines.FirstOrDefault(l => l.StartsWith("c="))?.Split(' ').LastOrDefault()?.Trim() ?? _serverIp;
//            string mediaLine = sdpLines.FirstOrDefault(l => l.StartsWith("m=video"));
//            int serverRtpPort = int.Parse(mediaLine?.Split(' ')[1] ?? "30264");
//            ssrc = sdpLines.FirstOrDefault(l => l.StartsWith("y="))?.Substring(2).Replace("\r", ""); // 获取 INVITE 的 SSRC

//            var time = (request.Body.Split('\n').FirstOrDefault(l => l.StartsWith("t="))?.Trim() ?? "0 0").Replace("t=", "");

//            Console.WriteLine($"Received INVITE SDP:\n{request.Body}");

//            //m=video {_localRtpPort} TCP/RTP/AVP 96

//            // 生成设备的 SDP，使用 TCP/RTP/AVP 和 PS 流
//            string sdp = $@"v=0
//o={_deviceId} 0 0 IN IP4 {_localIp}
//s=Play
//c=IN IP4 {_localIp}
//t={time}
//m=video {_localRtpPort} TCP/RTP/AVP 96
//a=setup:active
//a=connection:new
//a=rtpmap:96 PS/90000
//a=sendonly
//y={ssrc}
//f=v/2////a///";

//            okResponse.Body = sdp;
//            okResponse.Header.ContentLength = Encoding.UTF8.GetBytes(sdp).Length;

//            Console.WriteLine($"Sending 200 OK with SDP to {remoteEndPoint}");
//            Console.WriteLine($"SDP Content:\n{sdp}");
//            await _sipTransport.SendResponseAsync(okResponse);
//        }

//        //ffmpeg推流
//        private static void StartFfmpegPushStream(SIPRequest request)
//        {
//            string[] sdpLines = request.Body.Split('\n');
//            string serverIp = sdpLines.FirstOrDefault(l => l.StartsWith("c="))?.Split(' ').LastOrDefault()?.Trim() ?? _serverIp;
//            string mediaLine = sdpLines.FirstOrDefault(l => l.StartsWith("m=video"));
//            int serverRtpPort = int.Parse(mediaLine?.Split(' ')[1] ?? "30264"); // 从 INVITE SDP 获取服务器端口

//            Console.WriteLine($"Parsed SDP: Server IP = {serverIp}, Port = {serverRtpPort}");

//            // 使用 _localRtpPort 作为源端口，推送至服务器端口
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -f mpegts -local_rtpport {_localRtpPort} \"udp://{serverIp}:{serverRtpPort}\"";
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -f mpegts -local_rtpport {_localRtpPort} \"udp://{serverIp}:{serverRtpPort}\"";
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -f rtsp -an \"rtsp://{serverIp}:{serverRtpPort}/stream\"";
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v libx264 -b:v 2000k -r 25 -f mpegts -an -loglevel verbose \"udp://{serverIp}:{serverRtpPort}\"";
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v libx264 -b:v 2000k -r 25 -c:a aac -b:a 128k -f rtp -payload_type 96 -ssrc {ssrc} \"rtp://{serverIp}:{serverRtpPort}\"";
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v libx264 -b:v 2000k -r 25 -profile:v baseline -level 3.0 -an -f rtp -payload_type 98 -ssrc {ssrc} \"rtp://{serverIp}:{serverRtpPort}\"";
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -f rtp_mpegts -an \"tcp://{serverIp}:{serverRtpPort}\"";
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -f mpegts -an \"tcp://{serverIp}:{serverRtpPort}\"";
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -f mpegts -an -loglevel verbose \"tcp://{serverIp}:{serverRtpPort}\"";
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -f rtp_mpegts -payload_type 96 -ssrc {ssrc} -an -loglevel verbose \"tcp://{serverIp}:{serverRtpPort}\"";
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -c:a copy -f mpegts -loglevel verbose \"tcp://{serverIp}:{serverRtpPort}\"";

//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -an -f mpegts -loglevel verbose \"tcp://{serverIp}:{serverRtpPort}\"";

//            // 设置码率为 2000k，帧率为 30fps
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -c:a copy -b:v 2000k -r 30 -f mpegts -loglevel verbose \"tcp://{serverIp}:{serverRtpPort}\"";

//            // 设置码率为 2000k，帧率为 30fps，缓冲区大小为 4000k
//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -c:a copy -b:v 2000k -bufsize 8000k -r 30 -f mpegts -loglevel verbose \"tcp://{serverIp}:{serverRtpPort}\"";

//            //string ffmpegArgs = $"-re -r 30 -i \"{_mp4FilePath}\" -c:v libx264 -crf 23 -c:a copy -b:v 2000k -bufsize 8000k -r 30 -f mpegts -loglevel verbose \"tcp://{serverIp}:{serverRtpPort}\"";

//            string ffmpegArgs = $"-re -r 60 -i \"{_mp4FilePath}\" -c:v libx264 -crf 23 -g 10 -c:a copy -b:v 2000k -bufsize 8000k -r 60 -f mpegts -loglevel verbose \"tcp://{serverIp}:{serverRtpPort}\"";


//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -c:v copy -c:a copy -f mpegts -payload_type 96 -ssrc {ssrc} -an -loglevel verbose \"tcp://{serverIp}:{serverRtpPort}\"";


//            //string ffmpegArgs = $"-re -i {_mp4FilePath} -vcodec libx264 -acodec pcm_alaw -f mpegts -payload_type 96 -muxdelay 0  tcp://{_serverIp}:{serverRtpPort}";

//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -vcodec libx264 -acodec pcm_alaw -f mpegts -payload_type 96 -muxdelay 0 -loglevel debug tcp://{_serverIp}:{serverRtpPort}";

//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -vcodec libx264 -acodec pcm_alaw -f mpegts -payload_type 96 -flush_packets 0 -max_delay 0 tcp://{_serverIp}:{serverRtpPort}";

//            //string ffmpegArgs = $"-re -i \"{_mp4FilePath}\" -vcodec libx264 -acodec pcm_alaw -f mpegts -payload_type 96 -max_delay 0 -flush_packets 0 tcp://{_serverIp}:{serverRtpPort}";




//            Console.WriteLine($"Starting FFmpeg push stream: ffmpeg {ffmpegArgs}");

//            ProcessStartInfo psi = new ProcessStartInfo
//            {
//                FileName = "ffmpeg",
//                Arguments = ffmpegArgs,
//                RedirectStandardOutput = true,
//                RedirectStandardError = true,
//                UseShellExecute = false,
//                CreateNoWindow = true
//            };

//            try
//            {
//                _ffmpegProcess = new Process { StartInfo = psi };
//                _ffmpegProcess.EnableRaisingEvents = true;

//                _ffmpegProcess.OutputDataReceived += (sender, e) =>
//                {
//                    if (e.Data != null)
//                        Console.WriteLine($"FFmpeg Output: {e.Data}");
//                };
//                _ffmpegProcess.ErrorDataReceived += (sender, e) =>
//                {
//                    if (e.Data != null)
//                    {
//                        // 检查是否包含错误关键词
//                        if (e.Data.Contains("Error") || e.Data.Contains("failed") || e.Data.Contains("Invalid"))
//                        {
//                            Console.WriteLine($"FFmpeg Error: {e.Data}");
//                        }
//                        else
//                        {
//                            Console.WriteLine($"FFmpeg Info: {e.Data}");
//                        }
//                    }
//                };
//                _ffmpegProcess.Exited += (sender, e) =>
//                {
//                    Console.WriteLine($"FFmpeg process exited with code: {_ffmpegProcess.ExitCode}");
//                };

//                bool started = _ffmpegProcess.Start();
//                if (started)
//                {
//                    Console.WriteLine("FFmpeg process started successfully.");
//                    _ffmpegProcess.BeginOutputReadLine();
//                    _ffmpegProcess.BeginErrorReadLine();
//                }
//                else
//                {
//                    Console.WriteLine("Failed to start FFmpeg process.");
//                }

//                Task.Run(async () =>
//                {
//                    await Task.Run(() => _ffmpegProcess.WaitForExit());
//                    Console.WriteLine($"FFmpeg process completed with exit code: {_ffmpegProcess.ExitCode}");
//                });
//            }
//            catch (Exception ex)
//            {
//                Console.WriteLine($"Error starting FFmpeg: {ex.Message}");
//            }
//        }

//        // 从请求中提取SN
//        private static string GetSNFromRequest(string requestBody)
//        {
//            try
//            {
//                int snStart = requestBody.IndexOf("<SN>") + 4;
//                int snEnd = requestBody.IndexOf("</SN>");
//                return requestBody.Substring(snStart, snEnd - snStart);
//            }
//            catch
//            {
//                return "0";
//            }
//        }

//        // 发送 REGISTER 请求
//        private static async Task SendRegisterRequest(bool withAuth, string authHeader = null)
//        {
//            var sipUri = SIPURI.ParseSIPURI($"sip:{_deviceId}@{_serverIp}:{_serverPort}");
//            var regRequest = new SIPRequest(SIPMethodsEnum.REGISTER, sipUri);

//            regRequest.Header = new SIPHeader();

//            // 获取本地端点
//            var localEndPoint = _sipTransport.GetSIPChannels()[0].ListeningSIPEndPoint;
//            if (localEndPoint == null)
//            {
//                Console.WriteLine("Local SIP endpoint is null!");
//                return;
//            }

//            // 设置 From 和 To 头
//            regRequest.Header.From = new SIPFromHeader(null, sipUri, null);
//            regRequest.Header.To = new SIPToHeader(null, sipUri, null);
//            regRequest.Header.Contact = new List<SIPContactHeader>
//            {
//                new SIPContactHeader(null, new SIPURI(SIPSchemesEnum.sip, localEndPoint.Address, localEndPoint.Port) { User = _deviceId })
//            };
//            regRequest.Header.Vias.Via = new List<SIPViaHeader>
//            {
//                new SIPViaHeader(localEndPoint, CallProperties.CreateBranchId())
//            };
//            regRequest.Header.CSeq = withAuth ? 2 : 1;
//            regRequest.Header.CallId = Guid.NewGuid().ToString();
//            regRequest.Header.Expires = 3600;

//            // 如果需要认证，设置 Authorization 头
//            if (withAuth && authHeader != null)
//            {
//                regRequest.Header.AuthenticationHeaders = new List<SIPAuthenticationHeader>
//                {
//                    SIPAuthenticationHeader.ParseSIPAuthenticationHeader(SIPAuthorisationHeadersEnum.Authorize, authHeader)
//                };
//                Console.WriteLine($"Added Authorization header: {authHeader}");
//            }

//            // 指定目标端点并发送请求
//            var dstEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(IPAddress.Parse(_serverIp), _serverPort));
//            Console.WriteLine("Sending REGISTER request to: " + dstEndPoint);
//            await _sipTransport.SendRequestAsync(dstEndPoint, regRequest);
//        }

//        // 处理服务端返回的响应
//        private static Task OnResponseReceived(SIPEndPoint localEndPoint, SIPEndPoint remoteEndPoint, SIPResponse response)
//        {
//            Console.WriteLine($"Received response: {response.StatusCode} {response.ReasonPhrase}");

//            if (response.Status == SIPResponseStatusCodesEnum.Unauthorised)
//            {
//                Console.WriteLine("Received 401 Unauthorized, preparing authentication...");

//                if (response.Header.AuthenticationHeaders != null && response.Header.AuthenticationHeaders.Count > 0)
//                {
//                    var wwwAuthHeader = response.Header.AuthenticationHeaders[0];
//                    if (wwwAuthHeader != null)
//                    {
//                        string authHeader = ComputeDigestAuth(wwwAuthHeader, _deviceId, _serverpassword, "REGISTER", $"sip:{_serverId}@{wwwAuthHeader.SIPDigest.Realm}");
//                        Console.WriteLine($"Generated Authorization: {authHeader}");
//                        Task.Run(() => SendRegisterRequest(true, authHeader));
//                    }
//                }
//                else
//                {
//                    Console.WriteLine("No WWW-Authenticate header found in 401 response.");
//                }
//            }
//            else if (response.Status == SIPResponseStatusCodesEnum.Ok)
//            {
//                Console.WriteLine("Registration successful!");
//            }
//            else
//            {
//                Console.WriteLine($"Unexpected response: {response.Status}");
//            }

//            return Task.CompletedTask;
//        }

//        //md5加密
//        private static string ComputeDigestAuth(SIPAuthenticationHeader authHeader, string username, string password, string method, string uri)
//        {
//            string realm = authHeader.SIPDigest.Realm;
//            string nonce = authHeader.SIPDigest.Nonce;
//            string qop = authHeader.SIPDigest.Qop;

//            string ha1 = $"{username}:{realm}:{password}".ToMD5Hash();
//            Console.WriteLine($"HA1: {ha1}");
//            string ha2 = $"{method}:{uri}".ToMD5Hash();
//            Console.WriteLine($"HA2: {ha2}");

//            string response;
//            if (!string.IsNullOrEmpty(qop))
//            {
//                string cnonce = Guid.NewGuid().ToString("N").Substring(0, 8);
//                string nc = "00000001";
//                string responseInput = $"{ha1}:{nonce}:{nc}:{cnonce}:{qop}:{ha2}";
//                Console.WriteLine($"Response input: {responseInput}");
//                response = responseInput.ToMD5Hash();
//                Console.WriteLine($"Response: {response}");
//                return $"Digest username=\"{username}\" ,realm=\"{realm}\" ,nonce=\"{nonce}\" ,uri=\"{uri}\" ,response=\"{response}\" ,algorithm=MD5 ,qop={qop} ,cnonce=\"{cnonce}\" ,nc={nc}";
//            }
//            else
//            {
//                response = $"{ha1}:{nonce}:{ha2}".ToMD5Hash();
//                return $"Digest username=\"{username}\",realm=\"{realm}\",nonce=\"{nonce}\",uri=\"{uri}\",response=\"{response}\",algorithm=MD5";
//            }
//        }
//    }

//    public static class StringExtensions
//    {
//        public static string ToMD5Hash(this string input)
//        {
//            using (var md5 = System.Security.Cryptography.MD5.Create())
//            {
//                byte[] inputBytes = System.Text.Encoding.UTF8.GetBytes(input);
//                byte[] hashBytes = md5.ComputeHash(inputBytes);
//                return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
//            }
//        }
//    }
//} 
#endregion